Data Preperation

Importing Data


# owid_covid_data is provided by Our World in Data

owid_covid_data <- read.csv("https://covid.ourworldindata.org/data/owid-covid-data.csv")

# daily_report is provided by the National Institute for Communicable Diseases (NICD)

daily_report <- read.csv("https://raw.githubusercontent.com/dsfsi/covid19za/master/data/nicd_daily_national_report.csv")

# Prov_excess_deaths is provided by the South African Medical Research Council (SAMRC)

Prov_excess_deaths <- read.csv("https://raw.githubusercontent.com/dsfsi/covid19za/master/data/samrc_excess_deaths_province.csv")

# tests, provincerecCSV, provincedeathCSV and provincecasesCSV are provided by Coronavirus COVID-19 (2019-nCoV) Data Repository for South Africa, Data Science for Social Impact research group, Dr. Vukosi Marivate, University of Pretoria.

tests <- 
  read.csv("https://github.com/dsfsi/covid19za/raw/master/data/covid19za_timeline_testing.csv")

provincerecCSV <- read.csv("https://raw.githubusercontent.com/dsfsi/covid19za/master/data/covid19za_provincial_cumulative_timeline_recoveries.csv")

provincedeathCSV <- read.csv("https://raw.githubusercontent.com/dsfsi/covid19za/master/data/covid19za_provincial_cumulative_timeline_deaths.csv")

provincecasesCSV <- read.csv("https://raw.githubusercontent.com/dsfsi/covid19za/master/data/covid19za_provincial_cumulative_timeline_confirmed.csv")

# hospital_data is provided by the National Institute for Communicable Diseases (NICD)

hospital_data <- read.csv("https://raw.githubusercontent.com/dsfsi/covid19za/master/data/nicd_hospital_surveillance_data.csv")

# vaccinations is provided by Africa Data Hub

vaccinations <- 
  read.csv("http://api.mediahack.co.za/adh/sa-vaccinations.csv")

# weekly_hospital_admissions is provided by the National Institute for Communicable Diseases (NICD)

weekly_hospital_admissions <- read.csv("data_raw/weekly_hospital_admissions.csv")

# death_data is provided by the South African Medical Research Council (SAMRC) 

library(readxl)
total_death_data <- read_excel("data_raw/death_data.xlsx", 
    sheet = "Total deaths 1+yr", skip = 1)

colnames(total_death_data) <-
  c("id", "date", "all_cause", "natural", "unatural")

library(readxl)
death_data <- 
  read_excel("data_raw/death_data.xlsx", 
    sheet = "Weekly excesses", col_names = FALSE, 
    skip = 1, n_max = 2)


death_data2 <- 
  data.frame(t(death_data[-1]))

colnames(death_data2) <-
  death_data[ , 1]

colnames(death_data2) <-
  c("excess_death_totals", "location")

excess_deaths_RSA <- 
  filter(death_data2, location == "RSA")

excess_deaths_prov <- 
  filter(death_data2, location == "KZN" | location == "EC" | 
                               location == "GP" | location == "WC" | location == "LP" | 
                               location == "MP" | location == "FS" | location == "NW" | location == "NC")

excess_deaths_metro <- 
  filter(death_data2, location == "BUF" | location == "CPT" | 
                               location == "EKU" | location == "ETH" | location == "JHN" | 
                               location == "MAN" | location == "NMA" | location == "TSH")

Data wrangling


owid_setZA <- filter(owid_covid_data, location == "South Africa")
south_africa_data <- owid_setZA %>% slice(28: n())
south_africa_data <- arrange(south_africa_data, desc(date))
south_africa_data_refined <- select(south_africa_data, date, reproduction_rate)
sa_hospital_data_refined <- select(hospital_data, date, current_num_in_hospital)

new_daily_confirmed_case_data <- filter(owid_covid_data, location == "World" | location == "Europe" |
                      location == "North America" | location == "South America" |
                      location == "Asia" | location == "Africa" | location == "Oceania")

#convert dates from char format to date format
provincerecCSV$date <- dmy(provincerecCSV$date)

provincedeathCSV$date <- dmy(provincedeathCSV$date)

provincecasesCSV$date <- dmy(provincecasesCSV$date)

owid_setZA$date <- ymd(owid_setZA$date)

vaccinations$date <- ymd(vaccinations$date)

#Create dataframe for lockdown levels

lckdwn <- data.frame(
  dateStart = dmy(c("26 March 2020",
                    "1 May 2020",
                    "1 June 2020",
                    "18 August 2020",
                    "21 September 2020",
                    "29 December 2020", 
                    "1 March 2021", 
                    "31 May 2021", 
                    "16 June 2021", 
                    "28 June 2021")),
                     
  dateEnd = dmy(c("30 April 2020",
                  "31 May 2020",
                  "17 August 2020",
                  "20 September 2020",
                  "28 December 2020",
                  "28 February 2021",
                  "30 May 2021",
                  "15 June 2021",
                  "27 June 2021",
                  "11 July 2021")),
                     
  level = c("Level 5",
            "Level 4",
            "Level 3",
            "Level 2",
            "Level 1",
            "Level 3",
            "Level 1",
            "Level 2",
            "Level 3",
            "Level 4"))

Plotting Figures:

The daily vaccination data was used to create a bar chart with the last eight days’ values as an annotation. The rows for the annotation was filtered by using the slice_tail() function which allowed the latest values to be retrieved when the data gets updated. The ggdraw() function from the cowplot package was used to add the table to the graph.


#Create table for vaccination text annotation
vactexttbl <- 
  vaccinations %>%
  slice_tail(n = 8) %>%
  select(date, vaccinated_daily)

#plot
daily_vaccinations <-
  ggplot(vaccinations, aes(date, vaccinated_daily)) +
  geom_bar(stat = "identity",
           width = 0.6) +
  scale_y_continuous(labels = comma, 
                     breaks = breaks_width(50000)) +
  scale_x_date(date_labels = "%d %b") +
  labs(x="", y="", 
       title = "Daily Vaccinations") +
  theme(axis.ticks = element_blank()) 

#add vaccination table with plot 
ggdraw() +
  draw_plot(daily_vaccinations) +
  draw_plot(tableGrob(vactexttbl, cols = colnames(vactexttbl), rows = NULL), x = 0.3, width = 0.2, y = 0.1)

A line graph was used to display the total vaccinations to date.


#plot
total_vaccinations <-
  ggplot(vaccinations, aes(date, vaccinated_total)) +
  geom_line(stat = "identity",
            color = "red", 
            size = 1) +
  scale_y_continuous(labels = comma, 
                     breaks = breaks_width(500000)) +
  scale_x_date(date_labels = "%d/%m") +
  labs(x="", y="",
       title = "Total Vaccinations") +
  theme(axis.ticks = element_blank())

ggplotly(total_vaccinations, width = 1000)

The daily confirmed cases is plotted as a bar graph along with the 7-day rolling average as a line graph on the top. The lockdown levels is added as rectangles in the background coinciding with the dates.


#Calculate 7 day average
owid_setZA <- 
  owid_setZA %>%
  arrange(desc(date)) %>%
  mutate(count_7da = rollmean(new_cases, k = 7, fill = NA)) %>%
  ungroup()

#plot
daily_cases <-
  ggplot() +
  geom_bar(data = owid_setZA, aes(date, new_cases), 
           stat = "identity",
           width = 0.4,
           color = "grey50"
           ) +
  geom_line(data = owid_setZA, aes(date, count_7da),
            color="red",
            size = 1) +
  scale_y_continuous(labels = comma, 
                     breaks = breaks_width(10000)) +
  scale_x_date(date_labels = "%d %b",
               date_breaks = "1 month") +
  labs(title = "Daily Confirmed Cases", x="", y="") +
  theme(axis.ticks = element_blank()) +
  geom_rect(data = lckdwn,
          aes(xmin = dateStart,
          xmax = dateEnd,
          fill = level),
          ymin = 0,
          ymax = 100000,
          alpha = 0.15,
          show.legend = FALSE) +
  scale_fill_manual(values = c("Level 1" = "lightgreen", "Level 2" = "yellow2", "Level 3" = "orange", "Level 4" = "orangered", "Level 5" = "red"))

ggplotly(daily_cases, width = 1000) %>%
  layout(title = list(text = paste0('Daily Confirmed Cases',
                                    '<br>',
                                    '<br>',
                                    '<sup>',
                                    'This chart shows the daily confirmed cases since March 2020. The levels refer to the lockdown levels. Red line is the 7-day rolling average.',
                                    '</sup>')))
NA
NA

For the daily active case totals there was no data set providing the active cases day-by-day so it was calculated as follows active cases = total cases - total recovered - total deaths. The data first needed to be extracted from three different data sets and then joined by date to be able to calculate the daily active cases. The results were plotted in a bar graph.


#Select totals from death, recoveries and confirmed cases
recoveriesTotal <-
  provincerecCSV %>%
  select(date, total) %>%
  rename(recoveryTotal = total)

deathsTotal <- 
  provincedeathCSV %>%
  select(date, total) %>%
  rename(deathTotal = total)

confirmedTotal <- 
  provincecasesCSV %>%
  select(date, total) %>%
  rename(confirmedTotal = total)

#Join tables on date
provinceTotals <- 
  inner_join(recoveriesTotal, deathsTotal, on = date) %>%
  inner_join(confirmedTotal, on = date)

#Calculate daily active cases column (Active cases = total cases - total recovered - total deaths)
provinceTotals <- provinceTotals %>%
  mutate(activeCases = confirmedTotal - recoveryTotal - deathTotal)

#plot
daily_active <-
  ggplot(provinceTotals, aes(date, activeCases)) +
  geom_bar(stat = "identity",
           width = 0.5) +
  scale_y_continuous(labels = comma,
                     breaks = breaks_width(100000)) +
  scale_x_date(date_labels = "%d %b",
               date_breaks = "1 month") +
  labs(x="", y="",
       title = "Active Case Total by Day") +
  theme(axis.ticks = element_blank())

ggplotly(daily_active, width = 1000)

The daily deaths were plotted using a bar graph.


#plot
daily_deaths <-
  ggplot(owid_setZA, aes(date, new_deaths)) +
  geom_bar(stat = "identity",
           width = 0.5) +
  scale_y_continuous(labels = comma, 
                     breaks = breaks_width(200)) +
  scale_x_date(date_labels = "%d %b",
               date_breaks = "1 month") +
  labs(x="", y="",
       title = "Daily Deaths") +
  theme(axis.ticks = element_blank())

ggplotly(daily_deaths, width = 1000)

For the daily case trends by province the last eight months’ data was filtered. The data however only contained cumulative values and the daily values were calculated through the lag() function. Thereafter the data was converted from a ‘wide’ to ‘long’ format and the 7-day average were calculated for each province. The data is displayed as line graphs, faceted by province.


#Filter on last 8 months
provincecases <-
  provincecasesCSV %>% 
  filter(date > (today()-dmonths(8)))

#Calculate daily cases from cumulative cases
provincecases <-
  provincecases %>%  
  mutate(EC_daily= EC - lag(EC)) %>%
  mutate(FS_daily = FS - lag(FS)) %>%
  mutate(GP_daily = GP - lag(GP)) %>%
  mutate(KZN_daily = KZN - lag(KZN)) %>%
  mutate(LP_daily = LP - lag(LP)) %>%
  mutate(MP_daily = MP - lag(MP)) %>%
  mutate(NC_daily = NC - lag(NC)) %>%
  mutate(NW_daily = NW - lag(NW)) %>%
  mutate(WC_daily = WC - lag(WC)) 


#Select daily cases only  
provincecases <-
  provincecases %>% select(date, ends_with("daily"))

#Convert from wide to long data
dailyprovincecasesLong <-
  provincecases %>%
  gather(province, count, EC_daily:WC_daily)

#Calculate 7 day average
provincecases7da <- 
  dailyprovincecasesLong %>%
  group_by(province) %>% 
  mutate(count_7da = rollmean(count, k = 7, fill = NA)) %>%
  ungroup()

#plot
provincedaily <-
  ggplot(provincecases7da, aes(date, count_7da)) +
  geom_line(stat = "identity",
            color = "red", 
            size = 1) +
  labs(x="", y="",
       title = "Daily Case Trends") +
  scale_y_continuous() +
  scale_x_date(date_labels = "%d %b", 
               date_breaks = "4 month") +
  facet_wrap(~province, scales = "free_y") +
  theme(axis.title.y = element_blank(), 
        axis.text.y = element_blank(), 
        axis.ticks = element_blank())

ggplotly(provincedaily, width = 1000, height = 800)%>%
  layout(title = list(text = paste0('Daily Case Trends',
                                    '<br>',
                                    '<sup>',
                                    'These charts uses the seven-day average of daily new cases, for the past eight months, which evens out spikes',
                                    '</sup>')),
         margin=list(t = 75))

The total and active cases were plotted as line graphs on a single plot.


#plot
totalvsactive <-
  ggplot(provinceTotals) +
  geom_line( aes(date, confirmedTotal, 
                 color = "Cases"), 
            size = 1) +
  geom_line(aes(date, activeCases,
                color="Active Cases"),
            size = 1) +
    scale_color_manual(values = c(
    'Cases' = 'red',
    'Active Cases' = 'blue')) +
  scale_y_continuous(labels = comma) +
  scale_x_date(date_labels = "%d/%m", 
               date_breaks = "3 month") +
  labs(x="", y="",
       title = "Cases VS Active Cases",
       color = "") +
  theme(axis.ticks = element_blank())

ggplotly(totalvsactive, width = 1000) %>%
  layout(legend = list(x = 0.1, y = 0.9))

For the total deaths the data was plotted using a line graph along with the case fatality rate (CFR). The CFR is calculated using (Total deaths/ Total cases * 100). As the two data columns has different values the y-axis labels for the CFR, which is in percentage, needed to be adjusted manually. The last (maximum) value from the total death columns was also filtered to add as an annotation


#Calculate CFR (Total deaths/ Total cases * 100)
cfr <-
  owid_setZA %>%
  select(date, total_cases, total_deaths) %>%
  mutate(cfr_per = total_deaths/total_cases * 100)

#filter last values
deaths_end <- 
  cfr %>%
  arrange(date) %>%
  top_n(1, total_deaths)

#Set scale for secondary axis
scl = 10000

#plot
deathscfr <-
  ggplot(cfr, aes(x = date,
                  y = total_deaths)) +
  geom_line(aes(y = total_deaths, 
                color = "Total Deaths"), 
            size = 1) +
  geom_text_repel(label = deaths_end$total_deaths, 
                  data = deaths_end)+
  geom_line(aes(y = cfr_per*scl, color="Case Fatality Rate (CFR)"), 
            linetype = "dashed",
            size = 1) +
  scale_color_manual(values = c(
    'Total Deaths' = 'red',
    'Case Fatality Rate (CFR)' = 'blue')) +
  scale_y_continuous(labels = comma, 
                     breaks = breaks_width(20000), 
                     sec.axis = sec_axis(~./scl)) +
  scale_x_date(date_labels = "%d/%m", 
               date_breaks = "3 month") +
  labs(x="", y="",
       title = "Total Deaths",
       color = "") +
  theme(axis.ticks = element_blank(),
        legend.position = c(0.8, 0.2))

deathscfr

The doubling rate is essentially the number of days it takes for the cases/deaths to double in value. This was calculated using the formula in this article written by Swati B. Patel and Prakash Patel.


#Calculate doubling rates for confirmed cases & deaths
owid_setZA <- 
  owid_setZA %>%
  arrange(date) %>%
  mutate(death_dbl = 7 * (log(2) / 
                            (log(total_deaths /
                                   lag(total_deaths, n = 7))))) %>%
  mutate(cases_dbl = 7 * (log(2) / 
                            (log(total_cases /
                                   lag(total_cases, n = 7)))))

deathDB <-
  ggplot(owid_setZA) +
  geom_line( aes(x = date, y = death_dbl, 
                 color = "Deaths"),
             size = 1) +
  geom_line(aes(x = date,y = cases_dbl,
                color = "Confirmed Cases"),
            size = 1) +
  scale_color_manual(values = c(
    'Deaths' = 'red',
    'Confirmed Cases' = 'blue')) +
  labs(x="", y="",
       title = "Doubling Rates",
       color = "")

ggplotly(deathDB, width = 1000)%>%
  layout(title = list(text = paste0('Doubling Rates',
                                    '<br>',
                                    '<sup>',
                                    'Beta: Number of days for cases/deaths to double.',
                                    '</sup>')),
         legend = list(x = 0.1, y = 0.9))

For the total deaths by province the latest values (maximum) was selected, after this the data was converted to a ‘long’ format to be able to plot it as a histogram. The values were also added as annotations for each province.


#Select last row from deaths(max deaths)
Tprovincedeaths <- 
  provincedeathCSV %>%
  slice(n())

#Convert from wide to long data
TprovincedeathsLong <- 
  Tprovincedeaths %>%
  gather(province, count, EC:WC)

#plot
deathbyprovince <-
  ggplot(TprovincedeathsLong, aes(reorder(province, -count), count)) +
  geom_histogram(stat = "identity", 
                 fill = "red") +
  geom_text(aes(label = count),
            vjust = -0.3) +
  labs(x="", y="",
       title = "Deaths by Province") +
  theme(axis.ticks = element_blank())
  
deathbyprovince

To calculate the deaths per 100,000 population the values for each province where added in another column. The data was collected from the Stats SA website. The histogram was annotated with the values.


#Add province population according to http://www.statssa.gov.za/publications/P0302/P03022020.pdf
death100k <-
  TprovincedeathsLong %>%
  mutate(population = case_when(
    endsWith(province, "GP") ~ 15488137,
    endsWith(province, "KZN") ~ 11531628,
    endsWith(province, "WC") ~ 7005741,
    endsWith(province, "EC") ~ 6734001,
    endsWith(province, "LP") ~ 5852553,
    endsWith(province, "MP") ~ 4679786,
    endsWith(province, "NW") ~ 4108816,
    endsWith(province, "FS") ~ 2928903,
    endsWith(province, "NC") ~ 1292786))

#Calculate deaths/100k
death100k <-
  death100k %>%
  mutate(death100k = round(count/population * 100000, digits = 2))

#plot
death100kplot <-
  ggplot(death100k, aes(reorder(province, -count), death100k)) +
  geom_histogram(stat = "identity",
                 fill = "red") +
  geom_text(aes(label = death100k),
            vjust = -0.3) +
  labs(x="", y="",
       title = "Deaths by Province (per 100K)") +
  theme(axis.ticks = element_blank())
  
death100kplot

For the province infections per 100,000 population the latest (maximum) values where again selected, then converted to long format and the province population added as a column. The histogram was again annotated with the latest values.


#Select last row from cases (max cases)
infect100k <-
  provincecasesCSV %>%
  slice(n())
  

#Convert data from wide to long
infect100kLong <- 
  infect100k %>%
  gather(province, count, EC:WC)

#Add province population according to http://www.statssa.gov.za/publications/P0302/P03022020.pdf
infect100k <- 
  infect100kLong %>%
  mutate(population = case_when(
    endsWith(province, "GP") ~ 15488137,
    endsWith(province, "KZN") ~ 11531628,
    endsWith(province, "WC") ~ 7005741,
    endsWith(province, "EC") ~ 6734001,
    endsWith(province, "LP") ~ 5852553,
    endsWith(province, "MP") ~ 4679786,
    endsWith(province, "NW") ~ 4108816,
    endsWith(province, "FS") ~ 2928903,
    endsWith(province, "NC") ~ 1292786))

#Calculate cases/100k
infect100k <-
  infect100k %>%
  mutate(infect100k = round(count/population * 100000, digits = 2))

#plot
infect100kplot <-
  ggplot(infect100k, aes(y = reorder(province, infect100k), infect100k)) +
  geom_histogram(stat = "identity",
                 fill = "red") +
  geom_text(aes(label = infect100k),
            hjust = 1) +
  labs(x="", y="",
       title = "Province Infections per 100K Population") +
  theme(axis.ticks = element_blank())
  
infect100kplot

For the confirmed and active case trends by province plots the province- cases, deaths and recoveries data sets were each converted to a ‘long’ format where-after it was joined on the date. Again the formula, active cases = total cases - total recovered - total deaths, were used to calculate the active cases column. The data were then plotted as line graphs and faceted by province.


#Convert province cases, recoveries & deaths from wide to long

pcaseslong <-
  provincecasesCSV %>%
  select(date, EC:WC) %>%
  gather(province, confirmed_cases, EC:WC)

pdeathslong <-
  provincedeathCSV %>%
  select(date, EC:WC) %>%
  gather(province, confirmed_deaths, EC:WC)

precoverieslong <-
  provincerecCSV %>%
  select(date, EC:WC) %>%
  gather(province, confirmed_recoveries, EC:WC)

#Join tables on date
pactivecases <- 
  inner_join(pcaseslong, pdeathslong, on = date) %>% 
  inner_join(precoverieslong, on = date)

#Calculate daily active cases column (Active cases = total cases - total recovered - total deaths)
pactivecases <-
  pactivecases %>%
  mutate(active_cases = confirmed_cases - confirmed_recoveries - confirmed_deaths)

#plot
pactiveplot <-
  ggplot(pactivecases) +
  geom_line( aes(date, active_cases),
             color = "blue", 
             size = 1) +
  geom_line(aes(date, confirmed_cases),
            color = "red",
            size = 1) +
  labs(x="", y="",
       title = "Confirmed and Active Cases by Province") +
  scale_y_continuous(labels = comma) +
  facet_wrap(~province) +
  theme(axis.ticks = element_blank(),
        axis.text = element_blank())

ggplotly(pactiveplot, width = 1000, height = 800)

Bar chart showing the average daily tests per week. In order to plot the bar chart, data from the south_africa_data dataframe is needed. The data frame daily_test_per_week_df was created so that south_africa_data could be mutated by replacing “NA” values with 0. This was done in order to calculate weekly averages.

 
# Create a list where all "NA" values are replaced with 0 

daily_test_per_week_df <- south_africa_data %>% mutate_at(c(1:60), ~replace(., is.na(.), 0))

# Create list where data is grouped by week and the average of each week's daily new tests calculated

daily_test_per_week_df <- daily_test_per_week_df %>% group_by(week = cut(parse_date_time(date, "Y%/m%/d%"), "week")) %>% 
  summarise(value = mean(new_tests_smoothed))

# Plot bar chart

avg_daily_tests_per_week_plot <- 
  ggplot(daily_test_per_week_df, mapping = aes(x = reorder(week, desc(week)), y = value)) +
  geom_col(stat = "identity", position = "dodge" ,fill = "red") +
  geom_text(aes(label = round(value)), vjust = 0.1, hjust = 0)
  
# Flip the coordinates of the bar chart

avg_daily_tests_per_week_plot + coord_flip() +
  ggtitle("Average Daily Tests Per Week") +
  xlab("Week") + ylab("") +
  theme(axis.text.y = element_text(angle = 0))

Bar chart showing the average daily positive cases per week. In order to plot this bar chart, the “NA” values needed to be replaced as well to correctly calculate the weekly averages for daily positive cases.


#n1 <- 7 # every seven days

# create a list with the mean of daily positive cases per week

# test_per_positive_df <-south_africa_data %>% group_by(n = (row_number() -1) %/% n1) %>%
#                                  mutate(mean = mean(new_cases_smoothed))

# Create a list where all "NA" values are replaced with 0 

test_per_positive_df <- south_africa_data %>% mutate_at(c(1:60), ~replace(., is.na(.), 0))

# Create list where data is grouped by week and the average of each week's daily new tests calculated

test_per_positive_df <- test_per_positive_df %>% group_by(week = cut(parse_date_time(date, "Y%/m%/d%"), "week")) %>% 
  summarise(value = mean(new_cases_smoothed))

# Plot bar chart

ggplot(data = test_per_positive_df, mapping = aes(x = reorder(week, desc(week)), y = value)) + 
  geom_col(position = "dodge", fill = "red") +
  xlab("Week") + ylab("") + 
  ggtitle("Average Daily Positives Per Week") +  
  geom_text(aes(label = round(value)), vjust = 0.5, hjust = 0) +
  coord_flip()

Bar chart showing the number of tests conducted per positive case (weekly). In order to plot this bar chart, the “NA” values needed to be replaced as well to correctly calculate the weekly averages for the number of tests per positive case.


n1 <- 7 # every seven days

# create a list with the mean of test_per_positive case

# test_per_positive_df <-south_africa_data %>% group_by(n = (row_number() -1) %/% n1) %>%
#                                  mutate(mean = mean((new_tests_smoothed/ new_cases_smoothed)))

# Create a list where all "NA" values are replaced with 0 

test_per_positive_df <- south_africa_data %>% mutate_at(c(1:60), ~replace(., is.na(.), 0))

# Create list where data is grouped by week and the average of each week's daily new tests calculated

test_per_positive_df <- test_per_positive_df %>% group_by(week = cut(parse_date_time(date, "Y%/m%/d%"), "week")) %>% 
  summarise(value = mean(new_tests_smoothed) / mean(new_cases_smoothed))

# Plot bar chart

ggplot(data = test_per_positive_df, mapping = 
              aes(x = reorder(week, desc(week)), y = value)) + 
  geom_col(stat = "identity", position = "dodge", fill = "red") +
  xlab("Week") + ylab("") + 
  ggtitle("Number of Tests per Positive Case (Weekly)") +  
  geom_text(aes(label = round(value,2), vjust = 0.5, hjust = 0)) +
  coord_flip(expand = T)

Point chart showing the daily tests and positive cases. In order to plot this point chart, the data frame south_africa_data was used along with the ggplot package.


# Plot point chart

ggplot(data = south_africa_data, mapping = aes(x = round_date(parse_date_time(date, "Y%/m%/d%"), unit = "day"), y = new_tests_smoothed), size = new_cases_smoothed) + 
  geom_point(aes(size = new_cases_smoothed), col = "tomato3", alpha = 0.7, show.legend = F) +
  xlab("") + 
  ylab("Number of Tests per Day") +
  scale_radius() +
  ggtitle("Daily Tests and Positive Cases")

Bar chart showing the confirmed infections for the last 20 days. In order to plot this bar chart, the data frame confirmed_infections_last_twenty_days was created. This data frame was derived from the south_africa_data data frame which was filtered and arranged so that only the last twenty days’ confirmed infections were plotted in descending order of date.


confirmed_infections_last_twenty_days <- filter(arrange(south_africa_data, date), between(row_number(), n()-19, n()))
#confirmed_infections_last_twenty_days


ggplot(data = confirmed_infections_last_twenty_days, mapping = 
  aes(x = reorder(date, desc(date)), y = new_cases)) +
  geom_col(position = "stack", fill = "red") +
  xlab("") + ylab("") + 
  ggtitle(" Confirmed infections (last 20 days)") +  
  geom_text(aes(label = new_cases), vjust = 0.5, hjust = 0) +
  coord_flip()

# Under Construction

Bar chart showing the positivity rate: number of tests conducted versus the percentage positive cases. In order to plot this chart, which consists of both bar and line charts, the data frame pos_rate_df was used. This data frame was derived from the south_africa_data data frame after it was filtered and arranged by taking the last month’s rows.


# Prepare list for graph plot by filtering and sorting data from south_africa_data

df <-
  filter(arrange(south_africa_data, date), between(row_number(), n()-26, n()))

# Plot bar chart of number of tests and line chart of positivity rate

ggplot(df) +
  geom_col(aes(x = date, y = new_tests), size = 1, fill = "blue") +
  geom_line(aes(x = date, y = (positive_rate * 100)*3500), colour = "red", size = 1.5, group = 1) +
  scale_y_continuous(name = "Tests Conducted", sec.axis = sec_axis(~ . /3500, name = "Positive Tests (%)")) +
  theme(axis.text.x = element_text(angle = 90))

Line chart showing the national hospital admissions. In order to plot this line chart, data from the weekly_hospital_admissions data frame was used.


ggplot(weekly_hospital_admissions) +
  geom_line(mapping = aes(x = round_date(parse_date_time(week_start, "Y%/m%/d%"), unit = "week"), y = total_national_hospital_admissions), colour = "red") +
  ggtitle("National Hospital Admissions") +
  xlab("EPI WEEK") + ylab("")

# UNDER CONSTRUCTION

Table showing public versus private tests. In order to create this table, various data preparations and calculations were made, along with the plotly package.


# Prepare and calculate data for the creation of the plotly table

df <- 
  tests %>% 
  slice(n()-1)

latest_daily_report <- 
  tests %>% 
  slice(n())

total_tests_var <- 
  latest_daily_report$cumulative_tests

total_tests_var_percent <- 
  "100 (%)"

total_public_tests_var <- 
  latest_daily_report$cumulative_tests_public

total_public_tests_var_percent <- 
  round((total_public_tests_var/total_tests_var) * 100, 2)

total_private_tests_var <-
  latest_daily_report$cumulative_tests_private

total_private_tests_var_percent <-
  round((total_private_tests_var/total_tests_var) * 100, 2)

new_public_tests_var <- 
  latest_daily_report$cumulative_tests_public 
- df$cumulative_tests_public
[1] -5584413
new_private_tests_var <- 
  latest_daily_report$cumulative_tests_private 
- df$cumulative_tests_private
[1] -7258348
total_new_tests_var <- 
  new_public_tests_var + new_private_tests_var

new_public_tests_var_percent <- 
  round((new_public_tests_var/total_new_tests_var) * 100, 2)

new_private_tests_var_percent <- 
  round((new_private_tests_var/total_new_tests_var) * 100, 2)

tab <- 
  matrix(c(total_tests_var,total_tests_var_percent,total_public_tests_var, total_public_tests_var_percent, total_private_tests_var, total_private_tests_var_percent, new_public_tests_var, new_public_tests_var_percent, new_private_tests_var, new_private_tests_var_percent), ncol=2, byrow=TRUE)

rownames(tab) <- 
  c('Total Tests','Total Public Tests','Total Private Tests',
                   'New Public Tests', 'New Private Tests')
tab <- 
  as.table(tab)

public_vs_private_table <- 
  as.data.frame.matrix(tab)

# Create a plotly table for public_vs_private_table list

pvp <- 
  plot_ly(
  width = 1000,
  height = 500,
  type = 'table',
  header = list(
    values = c("<b>Public vs. Private Tests</b>", names(public_vs_private_table)),
    align = c('left', rep('center', ncol(public_vs_private_table))),
    line = list(width = 1, color = 'black'),
    fill = list(color = 'rgb(0, 165, 219)'),
    font = list(family = "Arial", size = 14, color = "white")
  ),
  cells = list(
    values = rbind(
      rownames(public_vs_private_table), 
      t(as.matrix(unname(public_vs_private_table)))
    ),
    align = c('left', rep('center', ncol(public_vs_private_table))),
    line = list(color = "black", width = 1),
    fill = list(color = c('rgb(0, 220, 219)', 'rgba(228, 222, 249, 0.65)')),
    font = list(family = "Arial", size = 12, color = c("black"))
  ))

pvp
NA

Point chart showing the current reproduction rate estimates for South Africa. In order to plot this point chart, the data frame south_africa_data_refined and the ggplot package was utilized. The data frame, south_africa_data_refined, was derived from the south_africa_data data frame. The data frame was modified so that it will only contain the date and reproduction_rate variables.


# Plot a point chart

ggplot(data = south_africa_data_refined, mapping = 
         aes(x = round_date(parse_date_time(date, "Y%/m%/d%"), unit = "day"), y = reproduction_rate, 
  group = 1)) + 
  geom_ribbon(aes(ymin = reproduction_rate - 0.1, ymax = reproduction_rate + 0.1), fill = "grey70") +
  geom_point(aes(colour = "red"), show.legend = F, position = position_dodge()) +
  ggtitle("Current Rt Estimates for South Africa") +
  xlab("") + 
  ylab("")

Bar chart showing the excess number of deaths in South Africa. In order to plot this bar chart, data from the data frame provincedeathCSV along with the ggplot and dplyer packages were used. The last row containing the covid-19 death totals for South Africa as a whole was sliced and stored in the data frame temp_df. Thereafter, a new column called excess_death_total_column with the excess death total for South Africa was added to temp_df. Thereafter, the bar charts were stacked on top of each other in order to illustrate the difference in excess total deaths and covid-19 total deaths for South Africa.


temp_df <- provincedeathCSV %>% slice(n())
temp_df <- temp_df %>%
  add_column(excess_death_total_column = excess_deaths_RSA$excess_death_totals)


ggplot(temp_df, aes(x = date)) +
  geom_col(mapping = aes(y = round(as.numeric(excess_death_total_column))), fill = "blue") +
  geom_col(temp_df, mapping = aes(y = total), fill = "red") +
  geom_text(aes(y = round(as.numeric(excess_death_total_column)), label = round(as.numeric(excess_death_total_column)), vjust = -0.5, hjust = 0.5)) +
  geom_text(aes(y = total, label = total), vjust = -0.5, hjust = 0.5) +
  ggtitle("Excess Deaths (South Africa)") +
  xlab("") + ylab("") + coord_cartesian(expand = T, ylim = c(0,200000))

Bar charts showing the excess deaths and covid-19 deaths per province in South Africa. In order to plot this bar chart, data from the data frame provincedeathCSV along with the ggplot and dplyer packages were used. The last row containing the covid-19 death totals per province was sliced and stored in the data frame df. Which was then converted from a data.frame to a data.table by reference with the use of the setDT() function. Thereafter, the data was transformed into a separate column via the stack() function. Unnecessary rows were removed in order for the tables excess_deaths_prov and temp to be merged into table joined_df by variables location and prov_code. Thereafter, the bar charts were stacked on top of each other in order to illustrate the difference in excess deaths and covid-19 deaths per province.


# Get the last row in data frame and store it in df

df <-
  provincedeathCSV %>% 
  slice(n())

# Convert data.frame into data.table by reference

setDT(df,keep.rownames = T)[]
   rn       date YYYYMMDD    EC   FS    GP   KZN   LP   MP   NC   NW    WC UNKNOWN total
1:  1 2021-06-26 20210626 11831 4859 12554 10659 2552 1508 1366 2235 12214       0 59778
             source
1: gis_nicd_scraper
# Transform data into a separate column

temp <- 
  stack(df)

# Remove unwanted rows

temp <-
  temp[-c(1,2,12, 13,14), ,drop = F]

# Rename column names

colnames(temp) <- 
  c("death_totals", "prov_code")

# Join data frames: excess_deaths_prov & temp and store it in joined_df

joined_df <- 
  merge(excess_deaths_prov, temp, by.x = "location", 
             by.y = "prov_code", all.x = TRUE, all.y = TRUE)

# Plot bar charts stacked on top of each other

ggplot(joined_df, mapping = aes(x = reorder(location, desc(as.numeric(excess_death_totals))), y = round(as.numeric(excess_death_totals)), digits = 2)) +
  geom_col(fill = "blue") +
  geom_col(joined_df, mapping = aes(y = as.numeric(death_totals)), fill = "red") +
  geom_text(aes(label = round(as.numeric(excess_death_totals)), vjust = -0.5, hjust = 0.5)) +
  ggtitle("Excess Deaths (Provinces)") +
  xlab("") + 
  ylab("") + 
  coord_cartesian(ylim = c(0,45000))

Bar chart showing the excess deaths in the metro areas in South Africa. In order to plot this chart, the data frame excess_deaths_metro was utilized along with the ggplot package. The bar chart is ordered from highest excess death count to lowest.


ggplot(excess_deaths_metro, mapping = aes(x = reorder(location, desc(as.numeric(excess_death_totals))), y = round(as.numeric(excess_death_totals)), digits = 2)) +
  geom_col(fill = "blue") +
  geom_text(aes(label = round(as.numeric(excess_death_totals)), vjust = -0.5, hjust = 0.5)) +
  ggtitle("Excess Deaths (Metros)") +
  xlab("") + ylab("") + coord_cartesian(expand = T, ylim = c(0,13000))

Line chart showing the new daily confirmed Covid-19 cases on a 7-day moving average scale for the regions: Africa, Asia, Europe, North America, Oceania, South America and the World. In order to plot this chart, data from the data frame new_daily_confirmed_case_data was used, along with the ggplot package. The new_daily_confirmed_case_data was derived from filtering the locations from the owid_covid_data data frame. The line chart was was grouped by location (i.e. region).


# Plot line graph
new_daily_confirmed_case_data
   iso_code continent location       date total_cases
1  OWID_AFR             Africa 2020-02-13          NA
2  OWID_AFR             Africa 2020-02-14           1
3  OWID_AFR             Africa 2020-02-15           1
4  OWID_AFR             Africa 2020-02-16           1
5  OWID_AFR             Africa 2020-02-17           1
6  OWID_AFR             Africa 2020-02-18           1
7  OWID_AFR             Africa 2020-02-19           1
8  OWID_AFR             Africa 2020-02-20           1
9  OWID_AFR             Africa 2020-02-21           1
10 OWID_AFR             Africa 2020-02-22           1
11 OWID_AFR             Africa 2020-02-23           1
12 OWID_AFR             Africa 2020-02-24           1
13 OWID_AFR             Africa 2020-02-25           2
14 OWID_AFR             Africa 2020-02-26           2
15 OWID_AFR             Africa 2020-02-27           2
16 OWID_AFR             Africa 2020-02-28           3
   new_cases new_cases_smoothed total_deaths new_deaths
1          0                 NA           NA          0
2          1                 NA           NA          0
3          0                 NA           NA          0
4          0                 NA           NA          0
5          0                 NA           NA          0
6          0                 NA           NA          0
7          0              0.143           NA          0
8          0              0.143           NA          0
9          0              0.000           NA          0
10         0              0.000           NA          0
11         0              0.000           NA          0
12         0              0.000           NA          0
13         1              0.143           NA          0
14         0              0.143           NA          0
15         0              0.143           NA          0
16         1              0.286           NA          0
   new_deaths_smoothed total_cases_per_million
1                   NA                      NA
2                   NA                   0.001
3                   NA                   0.001
4                   NA                   0.001
5                   NA                   0.001
6                   NA                   0.001
7                    0                   0.001
8                    0                   0.001
9                    0                   0.001
10                   0                   0.001
11                   0                   0.001
12                   0                   0.001
13                   0                   0.001
14                   0                   0.001
15                   0                   0.001
16                   0                   0.002
   new_cases_per_million new_cases_smoothed_per_million
1                  0.000                             NA
2                  0.001                             NA
3                  0.000                             NA
4                  0.000                             NA
5                  0.000                             NA
6                  0.000                             NA
7                  0.000                              0
8                  0.000                              0
9                  0.000                              0
10                 0.000                              0
11                 0.000                              0
12                 0.000                              0
13                 0.001                              0
14                 0.000                              0
15                 0.000                              0
16                 0.001                              0
   total_deaths_per_million new_deaths_per_million
1                        NA                      0
2                        NA                      0
3                        NA                      0
4                        NA                      0
5                        NA                      0
6                        NA                      0
7                        NA                      0
8                        NA                      0
9                        NA                      0
10                       NA                      0
11                       NA                      0
12                       NA                      0
13                       NA                      0
14                       NA                      0
15                       NA                      0
16                       NA                      0
   new_deaths_smoothed_per_million reproduction_rate
1                               NA                NA
2                               NA                NA
3                               NA                NA
4                               NA                NA
5                               NA                NA
6                               NA                NA
7                                0                NA
8                                0                NA
9                                0                NA
10                               0                NA
11                               0                NA
12                               0                NA
13                               0                NA
14                               0                NA
15                               0                NA
16                               0                NA
   icu_patients icu_patients_per_million hosp_patients
1            NA                       NA            NA
2            NA                       NA            NA
3            NA                       NA            NA
4            NA                       NA            NA
5            NA                       NA            NA
6            NA                       NA            NA
7            NA                       NA            NA
8            NA                       NA            NA
9            NA                       NA            NA
10           NA                       NA            NA
11           NA                       NA            NA
12           NA                       NA            NA
13           NA                       NA            NA
14           NA                       NA            NA
15           NA                       NA            NA
16           NA                       NA            NA
   hosp_patients_per_million weekly_icu_admissions
1                         NA                    NA
2                         NA                    NA
3                         NA                    NA
4                         NA                    NA
5                         NA                    NA
6                         NA                    NA
7                         NA                    NA
8                         NA                    NA
9                         NA                    NA
10                        NA                    NA
11                        NA                    NA
12                        NA                    NA
13                        NA                    NA
14                        NA                    NA
15                        NA                    NA
16                        NA                    NA
   weekly_icu_admissions_per_million weekly_hosp_admissions
1                                 NA                     NA
2                                 NA                     NA
3                                 NA                     NA
4                                 NA                     NA
5                                 NA                     NA
6                                 NA                     NA
7                                 NA                     NA
8                                 NA                     NA
9                                 NA                     NA
10                                NA                     NA
11                                NA                     NA
12                                NA                     NA
13                                NA                     NA
14                                NA                     NA
15                                NA                     NA
16                                NA                     NA
   weekly_hosp_admissions_per_million new_tests total_tests
1                                  NA        NA          NA
2                                  NA        NA          NA
3                                  NA        NA          NA
4                                  NA        NA          NA
5                                  NA        NA          NA
6                                  NA        NA          NA
7                                  NA        NA          NA
8                                  NA        NA          NA
9                                  NA        NA          NA
10                                 NA        NA          NA
11                                 NA        NA          NA
12                                 NA        NA          NA
13                                 NA        NA          NA
14                                 NA        NA          NA
15                                 NA        NA          NA
16                                 NA        NA          NA
   total_tests_per_thousand new_tests_per_thousand
1                        NA                     NA
2                        NA                     NA
3                        NA                     NA
4                        NA                     NA
5                        NA                     NA
6                        NA                     NA
7                        NA                     NA
8                        NA                     NA
9                        NA                     NA
10                       NA                     NA
11                       NA                     NA
12                       NA                     NA
13                       NA                     NA
14                       NA                     NA
15                       NA                     NA
16                       NA                     NA
   new_tests_smoothed new_tests_smoothed_per_thousand
1                  NA                              NA
2                  NA                              NA
3                  NA                              NA
4                  NA                              NA
5                  NA                              NA
6                  NA                              NA
7                  NA                              NA
8                  NA                              NA
9                  NA                              NA
10                 NA                              NA
11                 NA                              NA
12                 NA                              NA
13                 NA                              NA
14                 NA                              NA
15                 NA                              NA
16                 NA                              NA
   positive_rate tests_per_case tests_units
1             NA             NA            
2             NA             NA            
3             NA             NA            
4             NA             NA            
5             NA             NA            
6             NA             NA            
7             NA             NA            
8             NA             NA            
9             NA             NA            
10            NA             NA            
11            NA             NA            
12            NA             NA            
13            NA             NA            
14            NA             NA            
15            NA             NA            
16            NA             NA            
   total_vaccinations people_vaccinated
1                  NA                NA
2                  NA                NA
3                  NA                NA
4                  NA                NA
5                  NA                NA
6                  NA                NA
7                  NA                NA
8                  NA                NA
9                  NA                NA
10                 NA                NA
11                 NA                NA
12                 NA                NA
13                 NA                NA
14                 NA                NA
15                 NA                NA
16                 NA                NA
   people_fully_vaccinated new_vaccinations
1                       NA               NA
2                       NA               NA
3                       NA               NA
4                       NA               NA
5                       NA               NA
6                       NA               NA
7                       NA               NA
8                       NA               NA
9                       NA               NA
10                      NA               NA
11                      NA               NA
12                      NA               NA
13                      NA               NA
14                      NA               NA
15                      NA               NA
16                      NA               NA
   new_vaccinations_smoothed total_vaccinations_per_hundred
1                         NA                             NA
2                         NA                             NA
3                         NA                             NA
4                         NA                             NA
5                         NA                             NA
6                         NA                             NA
7                         NA                             NA
8                         NA                             NA
9                         NA                             NA
10                        NA                             NA
11                        NA                             NA
12                        NA                             NA
13                        NA                             NA
14                        NA                             NA
15                        NA                             NA
16                        NA                             NA
   people_vaccinated_per_hundred
1                             NA
2                             NA
3                             NA
4                             NA
5                             NA
6                             NA
7                             NA
8                             NA
9                             NA
10                            NA
11                            NA
12                            NA
13                            NA
14                            NA
15                            NA
16                            NA
   people_fully_vaccinated_per_hundred
1                                   NA
2                                   NA
3                                   NA
4                                   NA
5                                   NA
6                                   NA
7                                   NA
8                                   NA
9                                   NA
10                                  NA
11                                  NA
12                                  NA
13                                  NA
14                                  NA
15                                  NA
16                                  NA
   new_vaccinations_smoothed_per_million stringency_index
1                                     NA               NA
2                                     NA               NA
3                                     NA               NA
4                                     NA               NA
5                                     NA               NA
6                                     NA               NA
7                                     NA               NA
8                                     NA               NA
9                                     NA               NA
10                                    NA               NA
11                                    NA               NA
12                                    NA               NA
13                                    NA               NA
14                                    NA               NA
15                                    NA               NA
16                                    NA               NA
   population population_density median_age aged_65_older
1  1340598113                 NA         NA            NA
2  1340598113                 NA         NA            NA
3  1340598113                 NA         NA            NA
4  1340598113                 NA         NA            NA
5  1340598113                 NA         NA            NA
6  1340598113                 NA         NA            NA
7  1340598113                 NA         NA            NA
8  1340598113                 NA         NA            NA
9  1340598113                 NA         NA            NA
10 1340598113                 NA         NA            NA
11 1340598113                 NA         NA            NA
12 1340598113                 NA         NA            NA
13 1340598113                 NA         NA            NA
14 1340598113                 NA         NA            NA
15 1340598113                 NA         NA            NA
16 1340598113                 NA         NA            NA
   aged_70_older gdp_per_capita extreme_poverty
1             NA             NA              NA
2             NA             NA              NA
3             NA             NA              NA
4             NA             NA              NA
5             NA             NA              NA
6             NA             NA              NA
7             NA             NA              NA
8             NA             NA              NA
9             NA             NA              NA
10            NA             NA              NA
11            NA             NA              NA
12            NA             NA              NA
13            NA             NA              NA
14            NA             NA              NA
15            NA             NA              NA
16            NA             NA              NA
   cardiovasc_death_rate diabetes_prevalence female_smokers
1                     NA                  NA             NA
2                     NA                  NA             NA
3                     NA                  NA             NA
4                     NA                  NA             NA
5                     NA                  NA             NA
6                     NA                  NA             NA
7                     NA                  NA             NA
8                     NA                  NA             NA
9                     NA                  NA             NA
10                    NA                  NA             NA
11                    NA                  NA             NA
12                    NA                  NA             NA
13                    NA                  NA             NA
14                    NA                  NA             NA
15                    NA                  NA             NA
16                    NA                  NA             NA
   male_smokers handwashing_facilities
1            NA                     NA
2            NA                     NA
3            NA                     NA
4            NA                     NA
5            NA                     NA
6            NA                     NA
7            NA                     NA
8            NA                     NA
9            NA                     NA
10           NA                     NA
11           NA                     NA
12           NA                     NA
13           NA                     NA
14           NA                     NA
15           NA                     NA
16           NA                     NA
   hospital_beds_per_thousand life_expectancy
1                          NA              NA
2                          NA              NA
3                          NA              NA
4                          NA              NA
5                          NA              NA
6                          NA              NA
7                          NA              NA
8                          NA              NA
9                          NA              NA
10                         NA              NA
11                         NA              NA
12                         NA              NA
13                         NA              NA
14                         NA              NA
15                         NA              NA
16                         NA              NA
   human_development_index excess_mortality
1                       NA               NA
2                       NA               NA
3                       NA               NA
4                       NA               NA
5                       NA               NA
6                       NA               NA
7                       NA               NA
8                       NA               NA
9                       NA               NA
10                      NA               NA
11                      NA               NA
12                      NA               NA
13                      NA               NA
14                      NA               NA
15                      NA               NA
16                      NA               NA
 [ reached 'max' / getOption("max.print") -- omitted 3595 rows ]
ggplot(new_daily_confirmed_case_data, mapping = 
         aes(x = round_date(parse_date_time(date, "Y%/m%/d%"), unit = "day"), 
             y = new_cases_smoothed, 
             group = location)) + 
  geom_line(aes(colour = location)) +
  ggtitle("New Daily Confirmed Covid-19 Cases: 7-Day Average") +
  xlab("") + 
  ylab("") + 
  coord_cartesian(ylim = c(0,1000000))

LS0tDQp0aXRsZTogIjxjZW50ZXI+IDxoMT5Db3JvbmF2aXJ1cyBpbiBTb3V0aCBBZnJpY2E8L2gxPiA8L2NlbnRlcj4iDQphdXRob3I6ICI8Y2VudGVyPkdpbGxpYW0gVmFuIERlciBNZXJ3ZSAoMjE2NjA0MDkpICYgV2lsaGVsbSBWYW4gRGVyIE1lcndlICgyMjE4Mzc3OSk8Y2VudGVyPiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSApDQppZiAoIXJlcXVpcmUoInBhY21hbiIpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQ0KDQpwYWNtYW46OnBfbG9hZCgidGlkeXZlcnNlIiwNCiAgICAgICAgICAgICAgICJnZ3JlcGVsIiwgDQogICAgICAgICAgICAgICAic2NhbGVzIiwgDQogICAgICAgICAgICAgICAiem9vIiwgDQogICAgICAgICAgICAgICAibHVicmlkYXRlIiwgDQogICAgICAgICAgICAgICAiZ3JpZEV4dHJhIiwgDQogICAgICAgICAgICAgICAiY293cGxvdCIsIA0KICAgICAgICAgICAgICAgInBsb3RseSIsDQogICAgICAgICAgICAgICAicnZlc3QiLA0KICAgICAgICAgICAgICAgInN0cmluZ3IiLA0KICAgICAgICAgICAgICAgInJlYnVzIiwNCiAgICAgICAgICAgICAgICJyZWFkeGwiLA0KICAgICAgICAgICAgICAgImRwbHlyIiwNCiAgICAgICAgICAgICAgICJnZ3B1YnIiLA0KICAgICAgICAgICAgICAgImdncG1pc2MiLA0KICAgICAgICAgICAgICAgImRhdGEudGFibGUiLA0KICAgICAgICAgICAgICAgInJlc2hhcGUyIikNCmBgYA0KDQoNCiMgRGF0YSBQcmVwZXJhdGlvbg0KDQojIyBJbXBvcnRpbmcgRGF0YQ0KDQpgYGB7ciBSZWFkX0RhdGEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQ0KDQojIG93aWRfY292aWRfZGF0YSBpcyBwcm92aWRlZCBieSBPdXIgV29ybGQgaW4gRGF0YQ0KDQpvd2lkX2NvdmlkX2RhdGEgPC0gcmVhZC5jc3YoImh0dHBzOi8vY292aWQub3Vyd29ybGRpbmRhdGEub3JnL2RhdGEvb3dpZC1jb3ZpZC1kYXRhLmNzdiIpDQoNCiMgZGFpbHlfcmVwb3J0IGlzIHByb3ZpZGVkIGJ5IHRoZSBOYXRpb25hbCBJbnN0aXR1dGUgZm9yIENvbW11bmljYWJsZSBEaXNlYXNlcyAoTklDRCkNCg0KZGFpbHlfcmVwb3J0IDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZHNmc2kvY292aWQxOXphL21hc3Rlci9kYXRhL25pY2RfZGFpbHlfbmF0aW9uYWxfcmVwb3J0LmNzdiIpDQoNCiMgUHJvdl9leGNlc3NfZGVhdGhzIGlzIHByb3ZpZGVkIGJ5IHRoZSBTb3V0aCBBZnJpY2FuIE1lZGljYWwgUmVzZWFyY2ggQ291bmNpbCAoU0FNUkMpDQoNClByb3ZfZXhjZXNzX2RlYXRocyA8LSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2RzZnNpL2NvdmlkMTl6YS9tYXN0ZXIvZGF0YS9zYW1yY19leGNlc3NfZGVhdGhzX3Byb3ZpbmNlLmNzdiIpDQoNCiMgdGVzdHMsIHByb3ZpbmNlcmVjQ1NWLCBwcm92aW5jZWRlYXRoQ1NWIGFuZCBwcm92aW5jZWNhc2VzQ1NWIGFyZSBwcm92aWRlZCBieSBDb3JvbmF2aXJ1cyBDT1ZJRC0xOSAoMjAxOS1uQ29WKSBEYXRhIFJlcG9zaXRvcnkgZm9yIFNvdXRoIEFmcmljYSwgRGF0YSBTY2llbmNlIGZvciBTb2NpYWwgSW1wYWN0IHJlc2VhcmNoIGdyb3VwLCBEci4gVnVrb3NpIE1hcml2YXRlLCBVbml2ZXJzaXR5IG9mIFByZXRvcmlhLg0KDQp0ZXN0cyA8LSANCiAgcmVhZC5jc3YoImh0dHBzOi8vZ2l0aHViLmNvbS9kc2ZzaS9jb3ZpZDE5emEvcmF3L21hc3Rlci9kYXRhL2NvdmlkMTl6YV90aW1lbGluZV90ZXN0aW5nLmNzdiIpDQoNCnByb3ZpbmNlcmVjQ1NWIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZHNmc2kvY292aWQxOXphL21hc3Rlci9kYXRhL2NvdmlkMTl6YV9wcm92aW5jaWFsX2N1bXVsYXRpdmVfdGltZWxpbmVfcmVjb3Zlcmllcy5jc3YiKQ0KDQpwcm92aW5jZWRlYXRoQ1NWIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vZHNmc2kvY292aWQxOXphL21hc3Rlci9kYXRhL2NvdmlkMTl6YV9wcm92aW5jaWFsX2N1bXVsYXRpdmVfdGltZWxpbmVfZGVhdGhzLmNzdiIpDQoNCnByb3ZpbmNlY2FzZXNDU1YgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9kc2ZzaS9jb3ZpZDE5emEvbWFzdGVyL2RhdGEvY292aWQxOXphX3Byb3ZpbmNpYWxfY3VtdWxhdGl2ZV90aW1lbGluZV9jb25maXJtZWQuY3N2IikNCg0KIyBob3NwaXRhbF9kYXRhIGlzIHByb3ZpZGVkIGJ5IHRoZSBOYXRpb25hbCBJbnN0aXR1dGUgZm9yIENvbW11bmljYWJsZSBEaXNlYXNlcyAoTklDRCkNCg0KaG9zcGl0YWxfZGF0YSA8LSByZWFkLmNzdigiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2RzZnNpL2NvdmlkMTl6YS9tYXN0ZXIvZGF0YS9uaWNkX2hvc3BpdGFsX3N1cnZlaWxsYW5jZV9kYXRhLmNzdiIpDQoNCiMgdmFjY2luYXRpb25zIGlzIHByb3ZpZGVkIGJ5IEFmcmljYSBEYXRhIEh1Yg0KDQp2YWNjaW5hdGlvbnMgPC0gDQogIHJlYWQuY3N2KCJodHRwOi8vYXBpLm1lZGlhaGFjay5jby56YS9hZGgvc2EtdmFjY2luYXRpb25zLmNzdiIpDQoNCiMgd2Vla2x5X2hvc3BpdGFsX2FkbWlzc2lvbnMgaXMgcHJvdmlkZWQgYnkgdGhlIE5hdGlvbmFsIEluc3RpdHV0ZSBmb3IgQ29tbXVuaWNhYmxlIERpc2Vhc2VzIChOSUNEKQ0KDQp3ZWVrbHlfaG9zcGl0YWxfYWRtaXNzaW9ucyA8LSByZWFkLmNzdigiZGF0YV9yYXcvd2Vla2x5X2hvc3BpdGFsX2FkbWlzc2lvbnMuY3N2IikNCg0KIyBkZWF0aF9kYXRhIGlzIHByb3ZpZGVkIGJ5IHRoZSBTb3V0aCBBZnJpY2FuIE1lZGljYWwgUmVzZWFyY2ggQ291bmNpbCAoU0FNUkMpIA0KDQpsaWJyYXJ5KHJlYWR4bCkNCnRvdGFsX2RlYXRoX2RhdGEgPC0gcmVhZF9leGNlbCgiZGF0YV9yYXcvZGVhdGhfZGF0YS54bHN4IiwgDQogICAgc2hlZXQgPSAiVG90YWwgZGVhdGhzIDEreXIiLCBza2lwID0gMSkNCg0KY29sbmFtZXModG90YWxfZGVhdGhfZGF0YSkgPC0NCiAgYygiaWQiLCAiZGF0ZSIsICJhbGxfY2F1c2UiLCAibmF0dXJhbCIsICJ1bmF0dXJhbCIpDQoNCmxpYnJhcnkocmVhZHhsKQ0KZGVhdGhfZGF0YSA8LSANCiAgcmVhZF9leGNlbCgiZGF0YV9yYXcvZGVhdGhfZGF0YS54bHN4IiwgDQogICAgc2hlZXQgPSAiV2Vla2x5IGV4Y2Vzc2VzIiwgY29sX25hbWVzID0gRkFMU0UsIA0KICAgIHNraXAgPSAxLCBuX21heCA9IDIpDQoNCg0KZGVhdGhfZGF0YTIgPC0gDQogIGRhdGEuZnJhbWUodChkZWF0aF9kYXRhWy0xXSkpDQoNCmNvbG5hbWVzKGRlYXRoX2RhdGEyKSA8LQ0KICBkZWF0aF9kYXRhWyAsIDFdDQoNCmNvbG5hbWVzKGRlYXRoX2RhdGEyKSA8LQ0KICBjKCJleGNlc3NfZGVhdGhfdG90YWxzIiwgImxvY2F0aW9uIikNCg0KZXhjZXNzX2RlYXRoc19SU0EgPC0gDQogIGZpbHRlcihkZWF0aF9kYXRhMiwgbG9jYXRpb24gPT0gIlJTQSIpDQoNCmV4Y2Vzc19kZWF0aHNfcHJvdiA8LSANCiAgZmlsdGVyKGRlYXRoX2RhdGEyLCBsb2NhdGlvbiA9PSAiS1pOIiB8IGxvY2F0aW9uID09ICJFQyIgfCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbiA9PSAiR1AiIHwgbG9jYXRpb24gPT0gIldDIiB8IGxvY2F0aW9uID09ICJMUCIgfCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbiA9PSAiTVAiIHwgbG9jYXRpb24gPT0gIkZTIiB8IGxvY2F0aW9uID09ICJOVyIgfCBsb2NhdGlvbiA9PSAiTkMiKQ0KDQpleGNlc3NfZGVhdGhzX21ldHJvIDwtIA0KICBmaWx0ZXIoZGVhdGhfZGF0YTIsIGxvY2F0aW9uID09ICJCVUYiIHwgbG9jYXRpb24gPT0gIkNQVCIgfCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbiA9PSAiRUtVIiB8IGxvY2F0aW9uID09ICJFVEgiIHwgbG9jYXRpb24gPT0gIkpITiIgfCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbiA9PSAiTUFOIiB8IGxvY2F0aW9uID09ICJOTUEiIHwgbG9jYXRpb24gPT0gIlRTSCIpDQoNCg0KYGBgDQoNCiMjIERhdGEgd3JhbmdsaW5nDQoNCmBgYHtyIFByZXBhcmVfRGF0YSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9DQoNCm93aWRfc2V0WkEgPC0gDQogIGZpbHRlcihvd2lkX2NvdmlkX2RhdGEsIGxvY2F0aW9uID09ICJTb3V0aCBBZnJpY2EiKQ0KDQpzb3V0aF9hZnJpY2FfZGF0YSA8LSANCiAgb3dpZF9zZXRaQSAlPiUgc2xpY2UoMjg6IG4oKSkNCg0Kc291dGhfYWZyaWNhX2RhdGEgPC0gDQogIGFycmFuZ2Uoc291dGhfYWZyaWNhX2RhdGEsIGRlc2MoZGF0ZSkpDQoNCnNvdXRoX2FmcmljYV9kYXRhX3JlZmluZWQgPC0gDQogIHNlbGVjdChzb3V0aF9hZnJpY2FfZGF0YSwgZGF0ZSwgcmVwcm9kdWN0aW9uX3JhdGUpDQoNCnNhX2hvc3BpdGFsX2RhdGFfcmVmaW5lZCA8LSANCiAgc2VsZWN0KGhvc3BpdGFsX2RhdGEsIGRhdGUsIGN1cnJlbnRfbnVtX2luX2hvc3BpdGFsKQ0KDQpuZXdfZGFpbHlfY29uZmlybWVkX2Nhc2VfZGF0YSA8LSANCiAgZmlsdGVyKG93aWRfY292aWRfZGF0YSwgbG9jYXRpb24gPT0gIldvcmxkIiB8IGxvY2F0aW9uID09ICJFdXJvcGUiIHwNCiAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbiA9PSAiTm9ydGggQW1lcmljYSIgfCBsb2NhdGlvbiA9PSAiU291dGggQW1lcmljYSIgfA0KICAgICAgICAgICAgICAgICAgICAgIGxvY2F0aW9uID09ICJBc2lhIiB8IGxvY2F0aW9uID09ICJBZnJpY2EiIHwgbG9jYXRpb24gPT0gIk9jZWFuaWEiKQ0KDQojY29udmVydCBkYXRlcyBmcm9tIGNoYXIgZm9ybWF0IHRvIGRhdGUgZm9ybWF0DQpwcm92aW5jZXJlY0NTViRkYXRlIDwtIA0KICBkbXkocHJvdmluY2VyZWNDU1YkZGF0ZSkNCg0KcHJvdmluY2VkZWF0aENTViRkYXRlIDwtIA0KICBkbXkocHJvdmluY2VkZWF0aENTViRkYXRlKQ0KDQpwcm92aW5jZWNhc2VzQ1NWJGRhdGUgPC0gDQogIGRteShwcm92aW5jZWNhc2VzQ1NWJGRhdGUpDQoNCm93aWRfc2V0WkEkZGF0ZSA8LSANCiAgeW1kKG93aWRfc2V0WkEkZGF0ZSkNCg0KdmFjY2luYXRpb25zJGRhdGUgPC0gDQogIHltZCh2YWNjaW5hdGlvbnMkZGF0ZSkNCg0KYGBgDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQ0KDQojQ3JlYXRlIGRhdGFmcmFtZSBmb3IgbG9ja2Rvd24gbGV2ZWxzDQoNCmxja2R3biA8LSANCiAgZGF0YS5mcmFtZSgNCiAgZGF0ZVN0YXJ0ID0gZG15KGMoIjI2IE1hcmNoIDIwMjAiLA0KICAgICAgICAgICAgICAgICAgICAiMSBNYXkgMjAyMCIsDQogICAgICAgICAgICAgICAgICAgICIxIEp1bmUgMjAyMCIsDQogICAgICAgICAgICAgICAgICAgICIxOCBBdWd1c3QgMjAyMCIsDQogICAgICAgICAgICAgICAgICAgICIyMSBTZXB0ZW1iZXIgMjAyMCIsDQogICAgICAgICAgICAgICAgICAgICIyOSBEZWNlbWJlciAyMDIwIiwgDQogICAgICAgICAgICAgICAgICAgICIxIE1hcmNoIDIwMjEiLCANCiAgICAgICAgICAgICAgICAgICAgIjMxIE1heSAyMDIxIiwgDQogICAgICAgICAgICAgICAgICAgICIxNiBKdW5lIDIwMjEiLCANCiAgICAgICAgICAgICAgICAgICAgIjI4IEp1bmUgMjAyMSIpKSwNCiAgICAgICAgICAgICAgICAgICAgIA0KICBkYXRlRW5kID0gZG15KGMoIjMwIEFwcmlsIDIwMjAiLA0KICAgICAgICAgICAgICAgICAgIjMxIE1heSAyMDIwIiwNCiAgICAgICAgICAgICAgICAgICIxNyBBdWd1c3QgMjAyMCIsDQogICAgICAgICAgICAgICAgICAiMjAgU2VwdGVtYmVyIDIwMjAiLA0KICAgICAgICAgICAgICAgICAgIjI4IERlY2VtYmVyIDIwMjAiLA0KICAgICAgICAgICAgICAgICAgIjI4IEZlYnJ1YXJ5IDIwMjEiLA0KICAgICAgICAgICAgICAgICAgIjMwIE1heSAyMDIxIiwNCiAgICAgICAgICAgICAgICAgICIxNSBKdW5lIDIwMjEiLA0KICAgICAgICAgICAgICAgICAgIjI3IEp1bmUgMjAyMSIsDQogICAgICAgICAgICAgICAgICAiMTEgSnVseSAyMDIxIikpLA0KICAgICAgICAgICAgICAgICAgICAgDQogIGxldmVsID0gYygiTGV2ZWwgNSIsDQogICAgICAgICAgICAiTGV2ZWwgNCIsDQogICAgICAgICAgICAiTGV2ZWwgMyIsDQogICAgICAgICAgICAiTGV2ZWwgMiIsDQogICAgICAgICAgICAiTGV2ZWwgMSIsDQogICAgICAgICAgICAiTGV2ZWwgMyIsDQogICAgICAgICAgICAiTGV2ZWwgMSIsDQogICAgICAgICAgICAiTGV2ZWwgMiIsDQogICAgICAgICAgICAiTGV2ZWwgMyIsDQogICAgICAgICAgICAiTGV2ZWwgNCIpKQ0KDQpgYGANCg0KIyBQbG90dGluZyBGaWd1cmVzOg0KDQpUaGUgZGFpbHkgdmFjY2luYXRpb24gZGF0YSB3YXMgdXNlZCB0byBjcmVhdGUgYSBiYXIgY2hhcnQgd2l0aCB0aGUgbGFzdCBlaWdodCBkYXlzJyB2YWx1ZXMgYXMgYW4gYW5ub3RhdGlvbi4gVGhlIHJvd3MgZm9yIHRoZSBhbm5vdGF0aW9uIHdhcyBmaWx0ZXJlZCBieSB1c2luZyB0aGUgYHNsaWNlX3RhaWwoKWAgZnVuY3Rpb24gd2hpY2ggYWxsb3dlZCB0aGUgbGF0ZXN0IHZhbHVlcyB0byBiZSByZXRyaWV2ZWQgd2hlbiB0aGUgZGF0YSBnZXRzIHVwZGF0ZWQuIFRoZSBgZ2dkcmF3KClgIGZ1bmN0aW9uIGZyb20gdGhlIGBjb3dwbG90YCBwYWNrYWdlIHdhcyB1c2VkIHRvIGFkZCB0aGUgdGFibGUgdG8gdGhlIGdyYXBoLg0KDQpgYGB7ciwgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9NX0NCg0KI0NyZWF0ZSB0YWJsZSBmb3IgdmFjY2luYXRpb24gdGV4dCBhbm5vdGF0aW9uDQp2YWN0ZXh0dGJsIDwtIA0KICB2YWNjaW5hdGlvbnMgJT4lDQogIHNsaWNlX3RhaWwobiA9IDgpICU+JQ0KICBzZWxlY3QoZGF0ZSwgdmFjY2luYXRlZF9kYWlseSkNCg0KI3Bsb3QNCmRhaWx5X3ZhY2NpbmF0aW9ucyA8LQ0KICBnZ3Bsb3QodmFjY2luYXRpb25zLCBhZXMoZGF0ZSwgdmFjY2luYXRlZF9kYWlseSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsDQogICAgICAgICAgIHdpZHRoID0gMC42KSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSwgDQogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBicmVha3Nfd2lkdGgoNTAwMDApKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2xhYmVscyA9ICIlZCAlYiIpICsNCiAgbGFicyh4PSIiLCB5PSIiLCANCiAgICAgICB0aXRsZSA9ICJEYWlseSBWYWNjaW5hdGlvbnMiKSArDQogIHRoZW1lKGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpIA0KDQojYWRkIHZhY2NpbmF0aW9uIHRhYmxlIHdpdGggcGxvdCANCmdnZHJhdygpICsNCiAgZHJhd19wbG90KGRhaWx5X3ZhY2NpbmF0aW9ucykgKw0KICBkcmF3X3Bsb3QodGFibGVHcm9iKHZhY3RleHR0YmwsIGNvbHMgPSBjb2xuYW1lcyh2YWN0ZXh0dGJsKSwgcm93cyA9IE5VTEwpLCB4ID0gMC4zLCB3aWR0aCA9IDAuMiwgeSA9IDAuMSkNCg0KDQpgYGANCg0KQSBsaW5lIGdyYXBoIHdhcyB1c2VkIHRvIGRpc3BsYXkgdGhlIHRvdGFsIHZhY2NpbmF0aW9ucyB0byBkYXRlLg0KDQpgYGB7cn0NCg0KI3Bsb3QNCnRvdGFsX3ZhY2NpbmF0aW9ucyA8LQ0KICBnZ3Bsb3QodmFjY2luYXRpb25zLCBhZXMoZGF0ZSwgdmFjY2luYXRlZF90b3RhbCkpICsNCiAgZ2VvbV9saW5lKHN0YXQgPSAiaWRlbnRpdHkiLA0KICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgDQogICAgICAgICAgICBzaXplID0gMSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEsIA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYnJlYWtzX3dpZHRoKDUwMDAwMCkpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiVkLyVtIikgKw0KICBsYWJzKHg9IiIsIHk9IiIsDQogICAgICAgdGl0bGUgPSAiVG90YWwgVmFjY2luYXRpb25zIikgKw0KICB0aGVtZShheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpKQ0KDQpnZ3Bsb3RseSh0b3RhbF92YWNjaW5hdGlvbnMsIHdpZHRoID0gMTAwMCkNCg0KYGBgDQoNClRoZSBkYWlseSBjb25maXJtZWQgY2FzZXMgaXMgcGxvdHRlZCBhcyBhIGJhciBncmFwaCBhbG9uZyB3aXRoIHRoZSA3LWRheSByb2xsaW5nIGF2ZXJhZ2UgYXMgYSBsaW5lIGdyYXBoIG9uIHRoZSB0b3AuIFRoZSBsb2NrZG93biBsZXZlbHMgaXMgYWRkZWQgYXMgcmVjdGFuZ2xlcyBpbiB0aGUgYmFja2dyb3VuZCBjb2luY2lkaW5nIHdpdGggdGhlIGRhdGVzLg0KDQoNCmBgYHtyfQ0KDQojQ2FsY3VsYXRlIDcgZGF5IGF2ZXJhZ2UNCm93aWRfc2V0WkEgPC0gDQogIG93aWRfc2V0WkEgJT4lDQogIGFycmFuZ2UoZGVzYyhkYXRlKSkgJT4lDQogIG11dGF0ZShjb3VudF83ZGEgPSByb2xsbWVhbihuZXdfY2FzZXMsIGsgPSA3LCBmaWxsID0gTkEpKSAlPiUNCiAgdW5ncm91cCgpDQoNCiNwbG90DQpkYWlseV9jYXNlcyA8LQ0KICBnZ3Bsb3QoKSArDQogIGdlb21fYmFyKGRhdGEgPSBvd2lkX3NldFpBLCBhZXMoZGF0ZSwgbmV3X2Nhc2VzKSwgDQogICAgICAgICAgIHN0YXQgPSAiaWRlbnRpdHkiLA0KICAgICAgICAgICB3aWR0aCA9IDAuNCwNCiAgICAgICAgICAgY29sb3IgPSAiZ3JleTUwIg0KICAgICAgICAgICApICsNCiAgZ2VvbV9saW5lKGRhdGEgPSBvd2lkX3NldFpBLCBhZXMoZGF0ZSwgY291bnRfN2RhKSwNCiAgICAgICAgICAgIGNvbG9yPSJyZWQiLA0KICAgICAgICAgICAgc2l6ZSA9IDEpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hLCANCiAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGJyZWFrc193aWR0aCgxMDAwMCkpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiVkICViIiwNCiAgICAgICAgICAgICAgIGRhdGVfYnJlYWtzID0gIjEgbW9udGgiKSArDQogIGxhYnModGl0bGUgPSAiRGFpbHkgQ29uZmlybWVkIENhc2VzIiwgeD0iIiwgeT0iIikgKw0KICB0aGVtZShheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpKSArDQogIGdlb21fcmVjdChkYXRhID0gbGNrZHduLA0KICAgICAgICAgIGFlcyh4bWluID0gZGF0ZVN0YXJ0LA0KICAgICAgICAgIHhtYXggPSBkYXRlRW5kLA0KICAgICAgICAgIGZpbGwgPSBsZXZlbCksDQogICAgICAgICAgeW1pbiA9IDAsDQogICAgICAgICAgeW1heCA9IDEwMDAwMCwNCiAgICAgICAgICBhbHBoYSA9IDAuMTUsDQogICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJMZXZlbCAxIiA9ICJsaWdodGdyZWVuIiwgIkxldmVsIDIiID0gInllbGxvdzIiLCAiTGV2ZWwgMyIgPSAib3JhbmdlIiwgIkxldmVsIDQiID0gIm9yYW5nZXJlZCIsICJMZXZlbCA1IiA9ICJyZWQiKSkNCg0KZ2dwbG90bHkoZGFpbHlfY2FzZXMsIHdpZHRoID0gMTAwMCkgJT4lDQogIGxheW91dCh0aXRsZSA9IGxpc3QodGV4dCA9IHBhc3RlMCgnRGFpbHkgQ29uZmlybWVkIENhc2VzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8c3VwPicsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnVGhpcyBjaGFydCBzaG93cyB0aGUgZGFpbHkgY29uZmlybWVkIGNhc2VzIHNpbmNlIE1hcmNoIDIwMjAuIFRoZSBsZXZlbHMgcmVmZXIgdG8gdGhlIGxvY2tkb3duIGxldmVscy4gUmVkIGxpbmUgaXMgdGhlIDctZGF5IHJvbGxpbmcgYXZlcmFnZS4nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzwvc3VwPicpKSkNCg0KDQpgYGANCg0KRm9yIHRoZSBkYWlseSBhY3RpdmUgY2FzZSB0b3RhbHMgdGhlcmUgd2FzIG5vIGRhdGEgc2V0IHByb3ZpZGluZyB0aGUgYWN0aXZlIGNhc2VzIGRheS1ieS1kYXkgc28gaXQgd2FzIGNhbGN1bGF0ZWQgYXMgZm9sbG93cyBgYWN0aXZlIGNhc2VzID0gdG90YWwgY2FzZXMgLSB0b3RhbCByZWNvdmVyZWQgLSB0b3RhbCBkZWF0aHNgLiBUaGUgZGF0YSBmaXJzdCBuZWVkZWQgdG8gYmUgZXh0cmFjdGVkIGZyb20gdGhyZWUgZGlmZmVyZW50IGRhdGEgc2V0cyBhbmQgdGhlbiBqb2luZWQgYnkgZGF0ZSB0byBiZSBhYmxlIHRvIGNhbGN1bGF0ZSB0aGUgZGFpbHkgYWN0aXZlIGNhc2VzLiBUaGUgcmVzdWx0cyB3ZXJlIHBsb3R0ZWQgaW4gYSBiYXIgZ3JhcGguDQoNCmBgYHtyfQ0KDQojU2VsZWN0IHRvdGFscyBmcm9tIGRlYXRoLCByZWNvdmVyaWVzIGFuZCBjb25maXJtZWQgY2FzZXMNCnJlY292ZXJpZXNUb3RhbCA8LQ0KICBwcm92aW5jZXJlY0NTViAlPiUNCiAgc2VsZWN0KGRhdGUsIHRvdGFsKSAlPiUNCiAgcmVuYW1lKHJlY292ZXJ5VG90YWwgPSB0b3RhbCkNCg0KZGVhdGhzVG90YWwgPC0gDQogIHByb3ZpbmNlZGVhdGhDU1YgJT4lDQogIHNlbGVjdChkYXRlLCB0b3RhbCkgJT4lDQogIHJlbmFtZShkZWF0aFRvdGFsID0gdG90YWwpDQoNCmNvbmZpcm1lZFRvdGFsIDwtIA0KICBwcm92aW5jZWNhc2VzQ1NWICU+JQ0KICBzZWxlY3QoZGF0ZSwgdG90YWwpICU+JQ0KICByZW5hbWUoY29uZmlybWVkVG90YWwgPSB0b3RhbCkNCg0KI0pvaW4gdGFibGVzIG9uIGRhdGUNCnByb3ZpbmNlVG90YWxzIDwtIA0KICBpbm5lcl9qb2luKHJlY292ZXJpZXNUb3RhbCwgZGVhdGhzVG90YWwsIG9uID0gZGF0ZSkgJT4lDQogIGlubmVyX2pvaW4oY29uZmlybWVkVG90YWwsIG9uID0gZGF0ZSkNCg0KI0NhbGN1bGF0ZSBkYWlseSBhY3RpdmUgY2FzZXMgY29sdW1uIChBY3RpdmUgY2FzZXMgPSB0b3RhbCBjYXNlcyAtIHRvdGFsIHJlY292ZXJlZCAtIHRvdGFsIGRlYXRocykNCnByb3ZpbmNlVG90YWxzIDwtIHByb3ZpbmNlVG90YWxzICU+JQ0KICBtdXRhdGUoYWN0aXZlQ2FzZXMgPSBjb25maXJtZWRUb3RhbCAtIHJlY292ZXJ5VG90YWwgLSBkZWF0aFRvdGFsKQ0KDQojcGxvdA0KZGFpbHlfYWN0aXZlIDwtDQogIGdncGxvdChwcm92aW5jZVRvdGFscywgYWVzKGRhdGUsIGFjdGl2ZUNhc2VzKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwNCiAgICAgICAgICAgd2lkdGggPSAwLjUpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hLA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYnJlYWtzX3dpZHRoKDEwMDAwMCkpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiVkICViIiwNCiAgICAgICAgICAgICAgIGRhdGVfYnJlYWtzID0gIjEgbW9udGgiKSArDQogIGxhYnMoeD0iIiwgeT0iIiwNCiAgICAgICB0aXRsZSA9ICJBY3RpdmUgQ2FzZSBUb3RhbCBieSBEYXkiKSArDQogIHRoZW1lKGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpDQoNCmdncGxvdGx5KGRhaWx5X2FjdGl2ZSwgd2lkdGggPSAxMDAwKQ0KDQpgYGANCg0KVGhlIGRhaWx5IGRlYXRocyB3ZXJlIHBsb3R0ZWQgdXNpbmcgYSBiYXIgZ3JhcGguDQoNCmBgYHtyfQ0KDQojcGxvdA0KZGFpbHlfZGVhdGhzIDwtDQogIGdncGxvdChvd2lkX3NldFpBLCBhZXMoZGF0ZSwgbmV3X2RlYXRocykpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsDQogICAgICAgICAgIHdpZHRoID0gMC41KSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSwgDQogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBicmVha3Nfd2lkdGgoMjAwKSkgKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9sYWJlbHMgPSAiJWQgJWIiLA0KICAgICAgICAgICAgICAgZGF0ZV9icmVha3MgPSAiMSBtb250aCIpICsNCiAgbGFicyh4PSIiLCB5PSIiLA0KICAgICAgIHRpdGxlID0gIkRhaWx5IERlYXRocyIpICsNCiAgdGhlbWUoYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkNCg0KZ2dwbG90bHkoZGFpbHlfZGVhdGhzLCB3aWR0aCA9IDEwMDApDQoNCmBgYA0KDQpGb3IgdGhlIGRhaWx5IGNhc2UgdHJlbmRzIGJ5IHByb3ZpbmNlIHRoZSBsYXN0IGVpZ2h0IG1vbnRocycgZGF0YSB3YXMgZmlsdGVyZWQuIFRoZSBkYXRhIGhvd2V2ZXIgb25seSBjb250YWluZWQgY3VtdWxhdGl2ZSB2YWx1ZXMgYW5kIHRoZSBkYWlseSB2YWx1ZXMgd2VyZSBjYWxjdWxhdGVkIHRocm91Z2ggdGhlIGBsYWcoKWAgZnVuY3Rpb24uIFRoZXJlYWZ0ZXIgdGhlIGRhdGEgd2FzIGNvbnZlcnRlZCBmcm9tIGEgJ3dpZGUnIHRvICdsb25nJyBmb3JtYXQgYW5kIHRoZSA3LWRheSBhdmVyYWdlIHdlcmUgY2FsY3VsYXRlZCBmb3IgZWFjaCBwcm92aW5jZS4gVGhlIGRhdGEgaXMgZGlzcGxheWVkIGFzIGxpbmUgZ3JhcGhzLCBmYWNldGVkIGJ5IHByb3ZpbmNlLg0KDQpgYGB7cn0NCg0KI0ZpbHRlciBvbiBsYXN0IDggbW9udGhzDQpwcm92aW5jZWNhc2VzIDwtDQogIHByb3ZpbmNlY2FzZXNDU1YgJT4lIA0KICBmaWx0ZXIoZGF0ZSA+ICh0b2RheSgpLWRtb250aHMoOCkpKQ0KDQojQ2FsY3VsYXRlIGRhaWx5IGNhc2VzIGZyb20gY3VtdWxhdGl2ZSBjYXNlcw0KcHJvdmluY2VjYXNlcyA8LQ0KICBwcm92aW5jZWNhc2VzICU+JSAgDQogIG11dGF0ZShFQ19kYWlseT0gRUMgLSBsYWcoRUMpKSAlPiUNCiAgbXV0YXRlKEZTX2RhaWx5ID0gRlMgLSBsYWcoRlMpKSAlPiUNCiAgbXV0YXRlKEdQX2RhaWx5ID0gR1AgLSBsYWcoR1ApKSAlPiUNCiAgbXV0YXRlKEtaTl9kYWlseSA9IEtaTiAtIGxhZyhLWk4pKSAlPiUNCiAgbXV0YXRlKExQX2RhaWx5ID0gTFAgLSBsYWcoTFApKSAlPiUNCiAgbXV0YXRlKE1QX2RhaWx5ID0gTVAgLSBsYWcoTVApKSAlPiUNCiAgbXV0YXRlKE5DX2RhaWx5ID0gTkMgLSBsYWcoTkMpKSAlPiUNCiAgbXV0YXRlKE5XX2RhaWx5ID0gTlcgLSBsYWcoTlcpKSAlPiUNCiAgbXV0YXRlKFdDX2RhaWx5ID0gV0MgLSBsYWcoV0MpKSANCg0KDQojU2VsZWN0IGRhaWx5IGNhc2VzIG9ubHkgIA0KcHJvdmluY2VjYXNlcyA8LQ0KICBwcm92aW5jZWNhc2VzICU+JSBzZWxlY3QoZGF0ZSwgZW5kc193aXRoKCJkYWlseSIpKQ0KDQojQ29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBkYXRhDQpkYWlseXByb3ZpbmNlY2FzZXNMb25nIDwtDQogIHByb3ZpbmNlY2FzZXMgJT4lDQogIGdhdGhlcihwcm92aW5jZSwgY291bnQsIEVDX2RhaWx5OldDX2RhaWx5KQ0KDQojQ2FsY3VsYXRlIDcgZGF5IGF2ZXJhZ2UNCnByb3ZpbmNlY2FzZXM3ZGEgPC0gDQogIGRhaWx5cHJvdmluY2VjYXNlc0xvbmcgJT4lDQogIGdyb3VwX2J5KHByb3ZpbmNlKSAlPiUgDQogIG11dGF0ZShjb3VudF83ZGEgPSByb2xsbWVhbihjb3VudCwgayA9IDcsIGZpbGwgPSBOQSkpICU+JQ0KICB1bmdyb3VwKCkNCg0KI3Bsb3QNCnByb3ZpbmNlZGFpbHkgPC0NCiAgZ2dwbG90KHByb3ZpbmNlY2FzZXM3ZGEsIGFlcyhkYXRlLCBjb3VudF83ZGEpKSArDQogIGdlb21fbGluZShzdGF0ID0gImlkZW50aXR5IiwNCiAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIA0KICAgICAgICAgICAgc2l6ZSA9IDEpICsNCiAgbGFicyh4PSIiLCB5PSIiLA0KICAgICAgIHRpdGxlID0gIkRhaWx5IENhc2UgVHJlbmRzIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoKSArDQogIHNjYWxlX3hfZGF0ZShkYXRlX2xhYmVscyA9ICIlZCAlYiIsIA0KICAgICAgICAgICAgICAgZGF0ZV9icmVha3MgPSAiNCBtb250aCIpICsNCiAgZmFjZXRfd3JhcCh+cHJvdmluY2UsIHNjYWxlcyA9ICJmcmVlX3kiKSArDQogIHRoZW1lKGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkNCg0KZ2dwbG90bHkocHJvdmluY2VkYWlseSwgd2lkdGggPSAxMDAwLCBoZWlnaHQgPSA4MDApJT4lDQogIGxheW91dCh0aXRsZSA9IGxpc3QodGV4dCA9IHBhc3RlMCgnRGFpbHkgQ2FzZSBUcmVuZHMnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxicj4nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJzxzdXA+JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdUaGVzZSBjaGFydHMgdXNlcyB0aGUgc2V2ZW4tZGF5IGF2ZXJhZ2Ugb2YgZGFpbHkgbmV3IGNhc2VzLCBmb3IgdGhlIHBhc3QgZWlnaHQgbW9udGhzLCB3aGljaCBldmVucyBvdXQgc3Bpa2VzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8L3N1cD4nKSksDQogICAgICAgICBtYXJnaW49bGlzdCh0ID0gNzUpKQ0KDQpgYGANCg0KVGhlIHRvdGFsIGFuZCBhY3RpdmUgY2FzZXMgd2VyZSBwbG90dGVkIGFzIGxpbmUgZ3JhcGhzIG9uIGEgc2luZ2xlIHBsb3QuDQoNCmBgYHtyfQ0KDQojcGxvdA0KdG90YWx2c2FjdGl2ZSA8LQ0KICBnZ3Bsb3QocHJvdmluY2VUb3RhbHMpICsNCiAgZ2VvbV9saW5lKCBhZXMoZGF0ZSwgY29uZmlybWVkVG90YWwsIA0KICAgICAgICAgICAgICAgICBjb2xvciA9ICJDYXNlcyIpLCANCiAgICAgICAgICAgIHNpemUgPSAxKSArDQogIGdlb21fbGluZShhZXMoZGF0ZSwgYWN0aXZlQ2FzZXMsDQogICAgICAgICAgICAgICAgY29sb3I9IkFjdGl2ZSBDYXNlcyIpLA0KICAgICAgICAgICAgc2l6ZSA9IDEpICsNCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygNCiAgICAnQ2FzZXMnID0gJ3JlZCcsDQogICAgJ0FjdGl2ZSBDYXNlcycgPSAnYmx1ZScpKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKw0KICBzY2FsZV94X2RhdGUoZGF0ZV9sYWJlbHMgPSAiJWQvJW0iLCANCiAgICAgICAgICAgICAgIGRhdGVfYnJlYWtzID0gIjMgbW9udGgiKSArDQogIGxhYnMoeD0iIiwgeT0iIiwNCiAgICAgICB0aXRsZSA9ICJDYXNlcyBWUyBBY3RpdmUgQ2FzZXMiLA0KICAgICAgIGNvbG9yID0gIiIpICsNCiAgdGhlbWUoYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkNCg0KZ2dwbG90bHkodG90YWx2c2FjdGl2ZSwgd2lkdGggPSAxMDAwKSAlPiUNCiAgbGF5b3V0KGxlZ2VuZCA9IGxpc3QoeCA9IDAuMSwgeSA9IDAuOSkpDQoNCmBgYA0KDQpGb3IgdGhlIHRvdGFsIGRlYXRocyB0aGUgZGF0YSB3YXMgcGxvdHRlZCB1c2luZyBhIGxpbmUgZ3JhcGggYWxvbmcgd2l0aCB0aGUgY2FzZSBmYXRhbGl0eSByYXRlIChDRlIpLiBUaGUgQ0ZSIGlzIGNhbGN1bGF0ZWQgdXNpbmcgYChUb3RhbCBkZWF0aHMvIFRvdGFsIGNhc2VzICogMTAwKWAuIEFzIHRoZSB0d28gZGF0YSBjb2x1bW5zIGhhcyBkaWZmZXJlbnQgdmFsdWVzIHRoZSB5LWF4aXMgbGFiZWxzIGZvciB0aGUgQ0ZSLCB3aGljaCBpcyBpbiBwZXJjZW50YWdlLCBuZWVkZWQgdG8gYmUgYWRqdXN0ZWQgbWFudWFsbHkuIFRoZSBsYXN0IChtYXhpbXVtKSB2YWx1ZSBmcm9tIHRoZSB0b3RhbCBkZWF0aCBjb2x1bW5zIHdhcyBhbHNvIGZpbHRlcmVkIHRvIGFkZCBhcyBhbiBhbm5vdGF0aW9uIA0KDQpgYGB7ciwgZmlnLndpZHRoPTEwLGZpZy5oZWlnaHQ9NX0NCg0KI0NhbGN1bGF0ZSBDRlIgKFRvdGFsIGRlYXRocy8gVG90YWwgY2FzZXMgKiAxMDApDQpjZnIgPC0NCiAgb3dpZF9zZXRaQSAlPiUNCiAgc2VsZWN0KGRhdGUsIHRvdGFsX2Nhc2VzLCB0b3RhbF9kZWF0aHMpICU+JQ0KICBtdXRhdGUoY2ZyX3BlciA9IHRvdGFsX2RlYXRocy90b3RhbF9jYXNlcyAqIDEwMCkNCg0KI2ZpbHRlciBsYXN0IHZhbHVlcw0KZGVhdGhzX2VuZCA8LSANCiAgY2ZyICU+JQ0KICBhcnJhbmdlKGRhdGUpICU+JQ0KICB0b3BfbigxLCB0b3RhbF9kZWF0aHMpDQoNCiNTZXQgc2NhbGUgZm9yIHNlY29uZGFyeSBheGlzDQpzY2wgPSAxMDAwMA0KDQojcGxvdA0KZGVhdGhzY2ZyIDwtDQogIGdncGxvdChjZnIsIGFlcyh4ID0gZGF0ZSwNCiAgICAgICAgICAgICAgICAgIHkgPSB0b3RhbF9kZWF0aHMpKSArDQogIGdlb21fbGluZShhZXMoeSA9IHRvdGFsX2RlYXRocywgDQogICAgICAgICAgICAgICAgY29sb3IgPSAiVG90YWwgRGVhdGhzIiksIA0KICAgICAgICAgICAgc2l6ZSA9IDEpICsNCiAgZ2VvbV90ZXh0X3JlcGVsKGxhYmVsID0gZGVhdGhzX2VuZCR0b3RhbF9kZWF0aHMsIA0KICAgICAgICAgICAgICAgICAgZGF0YSA9IGRlYXRoc19lbmQpKw0KICBnZW9tX2xpbmUoYWVzKHkgPSBjZnJfcGVyKnNjbCwgY29sb3I9IkNhc2UgRmF0YWxpdHkgUmF0ZSAoQ0ZSKSIpLCANCiAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsDQogICAgICAgICAgICBzaXplID0gMSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygNCiAgICAnVG90YWwgRGVhdGhzJyA9ICdyZWQnLA0KICAgICdDYXNlIEZhdGFsaXR5IFJhdGUgKENGUiknID0gJ2JsdWUnKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEsIA0KICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYnJlYWtzX3dpZHRoKDIwMDAwKSwgDQogICAgICAgICAgICAgICAgICAgICBzZWMuYXhpcyA9IHNlY19heGlzKH4uL3NjbCkpICsNCiAgc2NhbGVfeF9kYXRlKGRhdGVfbGFiZWxzID0gIiVkLyVtIiwgDQogICAgICAgICAgICAgICBkYXRlX2JyZWFrcyA9ICIzIG1vbnRoIikgKw0KICBsYWJzKHg9IiIsIHk9IiIsDQogICAgICAgdGl0bGUgPSAiVG90YWwgRGVhdGhzIiwNCiAgICAgICBjb2xvciA9ICIiKSArDQogIHRoZW1lKGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LCAwLjIpKQ0KDQpkZWF0aHNjZnINCmBgYA0KDQpUaGUgZG91YmxpbmcgcmF0ZSBpcyBlc3NlbnRpYWxseSB0aGUgbnVtYmVyIG9mIGRheXMgaXQgdGFrZXMgZm9yIHRoZSBjYXNlcy9kZWF0aHMgdG8gZG91YmxlIGluIHZhbHVlLiBUaGlzIHdhcyBjYWxjdWxhdGVkIHVzaW5nIHRoZSBmb3JtdWxhIGluIHRoaXMgW2FydGljbGVdKGh0dHA6Ly9uamNtaW5kaWEub3JnL3VwbG9hZHMvMTEtM18xNDEtMTQzLnBkZikgd3JpdHRlbiBieSBTd2F0aSBCLiBQYXRlbCBhbmQgUHJha2FzaCBQYXRlbC4NCg0KYGBge3J9DQoNCiNDYWxjdWxhdGUgZG91YmxpbmcgcmF0ZXMgZm9yIGNvbmZpcm1lZCBjYXNlcyAmIGRlYXRocw0Kb3dpZF9zZXRaQSA8LSANCiAgb3dpZF9zZXRaQSAlPiUNCiAgYXJyYW5nZShkYXRlKSAlPiUNCiAgbXV0YXRlKGRlYXRoX2RibCA9IDcgKiAobG9nKDIpIC8gDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKGxvZyh0b3RhbF9kZWF0aHMgLw0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWcodG90YWxfZGVhdGhzLCBuID0gNykpKSkpICU+JQ0KICBtdXRhdGUoY2FzZXNfZGJsID0gNyAqIChsb2coMikgLyANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAobG9nKHRvdGFsX2Nhc2VzIC8NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFnKHRvdGFsX2Nhc2VzLCBuID0gNykpKSkpDQoNCmRlYXRoREIgPC0NCiAgZ2dwbG90KG93aWRfc2V0WkEpICsNCiAgZ2VvbV9saW5lKCBhZXMoeCA9IGRhdGUsIHkgPSBkZWF0aF9kYmwsIA0KICAgICAgICAgICAgICAgICBjb2xvciA9ICJEZWF0aHMiKSwNCiAgICAgICAgICAgICBzaXplID0gMSkgKw0KICBnZW9tX2xpbmUoYWVzKHggPSBkYXRlLHkgPSBjYXNlc19kYmwsDQogICAgICAgICAgICAgICAgY29sb3IgPSAiQ29uZmlybWVkIENhc2VzIiksDQogICAgICAgICAgICBzaXplID0gMSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygNCiAgICAnRGVhdGhzJyA9ICdyZWQnLA0KICAgICdDb25maXJtZWQgQ2FzZXMnID0gJ2JsdWUnKSkgKw0KICBsYWJzKHg9IiIsIHk9IiIsDQogICAgICAgdGl0bGUgPSAiRG91YmxpbmcgUmF0ZXMiLA0KICAgICAgIGNvbG9yID0gIiIpDQoNCmdncGxvdGx5KGRlYXRoREIsIHdpZHRoID0gMTAwMCklPiUNCiAgbGF5b3V0KHRpdGxlID0gbGlzdCh0ZXh0ID0gcGFzdGUwKCdEb3VibGluZyBSYXRlcycsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPGJyPicsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPHN1cD4nLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ0JldGE6IE51bWJlciBvZiBkYXlzIGZvciBjYXNlcy9kZWF0aHMgdG8gZG91YmxlLicsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnPC9zdXA+JykpLA0KICAgICAgICAgbGVnZW5kID0gbGlzdCh4ID0gMC4xLCB5ID0gMC45KSkNCmBgYA0KDQpGb3IgdGhlIHRvdGFsIGRlYXRocyBieSBwcm92aW5jZSB0aGUgbGF0ZXN0IHZhbHVlcyAobWF4aW11bSkgd2FzIHNlbGVjdGVkLCBhZnRlciB0aGlzIHRoZSBkYXRhIHdhcyBjb252ZXJ0ZWQgdG8gYSAnbG9uZycgZm9ybWF0IHRvIGJlIGFibGUgdG8gcGxvdCBpdCBhcyBhIGhpc3RvZ3JhbS4gVGhlIHZhbHVlcyB3ZXJlIGFsc28gYWRkZWQgYXMgYW5ub3RhdGlvbnMgZm9yIGVhY2ggcHJvdmluY2UuDQoNCmBgYHtyfQ0KDQojU2VsZWN0IGxhc3Qgcm93IGZyb20gZGVhdGhzKG1heCBkZWF0aHMpDQpUcHJvdmluY2VkZWF0aHMgPC0gDQogIHByb3ZpbmNlZGVhdGhDU1YgJT4lDQogIHNsaWNlKG4oKSkNCg0KI0NvbnZlcnQgZnJvbSB3aWRlIHRvIGxvbmcgZGF0YQ0KVHByb3ZpbmNlZGVhdGhzTG9uZyA8LSANCiAgVHByb3ZpbmNlZGVhdGhzICU+JQ0KICBnYXRoZXIocHJvdmluY2UsIGNvdW50LCBFQzpXQykNCg0KI3Bsb3QNCmRlYXRoYnlwcm92aW5jZSA8LQ0KICBnZ3Bsb3QoVHByb3ZpbmNlZGVhdGhzTG9uZywgYWVzKHJlb3JkZXIocHJvdmluY2UsIC1jb3VudCksIGNvdW50KSkgKw0KICBnZW9tX2hpc3RvZ3JhbShzdGF0ID0gImlkZW50aXR5IiwgDQogICAgICAgICAgICAgICAgIGZpbGwgPSAicmVkIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gY291bnQpLA0KICAgICAgICAgICAgdmp1c3QgPSAtMC4zKSArDQogIGxhYnMoeD0iIiwgeT0iIiwNCiAgICAgICB0aXRsZSA9ICJEZWF0aHMgYnkgUHJvdmluY2UiKSArDQogIHRoZW1lKGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpDQogIA0KZGVhdGhieXByb3ZpbmNlDQpgYGANCg0KVG8gY2FsY3VsYXRlIHRoZSBkZWF0aHMgcGVyIDEwMCwwMDAgcG9wdWxhdGlvbiB0aGUgdmFsdWVzIGZvciBlYWNoIHByb3ZpbmNlIHdoZXJlIGFkZGVkIGluIGFub3RoZXIgY29sdW1uLiBUaGUgZGF0YSB3YXMgY29sbGVjdGVkIGZyb20gdGhlIFN0YXRzIFNBIFt3ZWJzaXRlXShodHRwOi8vd3d3LnN0YXRzc2EuZ292LnphL3B1YmxpY2F0aW9ucy9QMDMwMi9QMDMwMjIwMjAucGRmKS4gVGhlIGhpc3RvZ3JhbSB3YXMgYW5ub3RhdGVkIHdpdGggdGhlIHZhbHVlcy4NCg0KYGBge3J9DQoNCiNBZGQgcHJvdmluY2UgcG9wdWxhdGlvbiBhY2NvcmRpbmcgdG8gaHR0cDovL3d3dy5zdGF0c3NhLmdvdi56YS9wdWJsaWNhdGlvbnMvUDAzMDIvUDAzMDIyMDIwLnBkZg0KZGVhdGgxMDBrIDwtDQogIFRwcm92aW5jZWRlYXRoc0xvbmcgJT4lDQogIG11dGF0ZShwb3B1bGF0aW9uID0gY2FzZV93aGVuKA0KICAgIGVuZHNXaXRoKHByb3ZpbmNlLCAiR1AiKSB+IDE1NDg4MTM3LA0KICAgIGVuZHNXaXRoKHByb3ZpbmNlLCAiS1pOIikgfiAxMTUzMTYyOCwNCiAgICBlbmRzV2l0aChwcm92aW5jZSwgIldDIikgfiA3MDA1NzQxLA0KICAgIGVuZHNXaXRoKHByb3ZpbmNlLCAiRUMiKSB+IDY3MzQwMDEsDQogICAgZW5kc1dpdGgocHJvdmluY2UsICJMUCIpIH4gNTg1MjU1MywNCiAgICBlbmRzV2l0aChwcm92aW5jZSwgIk1QIikgfiA0Njc5Nzg2LA0KICAgIGVuZHNXaXRoKHByb3ZpbmNlLCAiTlciKSB+IDQxMDg4MTYsDQogICAgZW5kc1dpdGgocHJvdmluY2UsICJGUyIpIH4gMjkyODkwMywNCiAgICBlbmRzV2l0aChwcm92aW5jZSwgIk5DIikgfiAxMjkyNzg2KSkNCg0KI0NhbGN1bGF0ZSBkZWF0aHMvMTAwaw0KZGVhdGgxMDBrIDwtDQogIGRlYXRoMTAwayAlPiUNCiAgbXV0YXRlKGRlYXRoMTAwayA9IHJvdW5kKGNvdW50L3BvcHVsYXRpb24gKiAxMDAwMDAsIGRpZ2l0cyA9IDIpKQ0KDQojcGxvdA0KZGVhdGgxMDBrcGxvdCA8LQ0KICBnZ3Bsb3QoZGVhdGgxMDBrLCBhZXMocmVvcmRlcihwcm92aW5jZSwgLWNvdW50KSwgZGVhdGgxMDBrKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShzdGF0ID0gImlkZW50aXR5IiwNCiAgICAgICAgICAgICAgICAgZmlsbCA9ICJyZWQiKSArDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBkZWF0aDEwMGspLA0KICAgICAgICAgICAgdmp1c3QgPSAtMC4zKSArDQogIGxhYnMoeD0iIiwgeT0iIiwNCiAgICAgICB0aXRsZSA9ICJEZWF0aHMgYnkgUHJvdmluY2UgKHBlciAxMDBLKSIpICsNCiAgdGhlbWUoYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSkNCiAgDQpkZWF0aDEwMGtwbG90DQoNCmBgYA0KDQpGb3IgdGhlIHByb3ZpbmNlIGluZmVjdGlvbnMgcGVyIDEwMCwwMDAgcG9wdWxhdGlvbiB0aGUgbGF0ZXN0IChtYXhpbXVtKSB2YWx1ZXMgd2hlcmUgYWdhaW4gc2VsZWN0ZWQsIHRoZW4gY29udmVydGVkIHRvIGxvbmcgZm9ybWF0IGFuZCB0aGUgcHJvdmluY2UgcG9wdWxhdGlvbiBhZGRlZCBhcyBhIGNvbHVtbi4gVGhlIGhpc3RvZ3JhbSB3YXMgYWdhaW4gYW5ub3RhdGVkIHdpdGggdGhlIGxhdGVzdCB2YWx1ZXMuDQoNCmBgYHtyfQ0KDQojU2VsZWN0IGxhc3Qgcm93IGZyb20gY2FzZXMgKG1heCBjYXNlcykNCmluZmVjdDEwMGsgPC0NCiAgcHJvdmluY2VjYXNlc0NTViAlPiUNCiAgc2xpY2UobigpKQ0KICANCg0KI0NvbnZlcnQgZGF0YSBmcm9tIHdpZGUgdG8gbG9uZw0KaW5mZWN0MTAwa0xvbmcgPC0gDQogIGluZmVjdDEwMGsgJT4lDQogIGdhdGhlcihwcm92aW5jZSwgY291bnQsIEVDOldDKQ0KDQojQWRkIHByb3ZpbmNlIHBvcHVsYXRpb24gYWNjb3JkaW5nIHRvIGh0dHA6Ly93d3cuc3RhdHNzYS5nb3YuemEvcHVibGljYXRpb25zL1AwMzAyL1AwMzAyMjAyMC5wZGYNCmluZmVjdDEwMGsgPC0gDQogIGluZmVjdDEwMGtMb25nICU+JQ0KICBtdXRhdGUocG9wdWxhdGlvbiA9IGNhc2Vfd2hlbigNCiAgICBlbmRzV2l0aChwcm92aW5jZSwgIkdQIikgfiAxNTQ4ODEzNywNCiAgICBlbmRzV2l0aChwcm92aW5jZSwgIktaTiIpIH4gMTE1MzE2MjgsDQogICAgZW5kc1dpdGgocHJvdmluY2UsICJXQyIpIH4gNzAwNTc0MSwNCiAgICBlbmRzV2l0aChwcm92aW5jZSwgIkVDIikgfiA2NzM0MDAxLA0KICAgIGVuZHNXaXRoKHByb3ZpbmNlLCAiTFAiKSB+IDU4NTI1NTMsDQogICAgZW5kc1dpdGgocHJvdmluY2UsICJNUCIpIH4gNDY3OTc4NiwNCiAgICBlbmRzV2l0aChwcm92aW5jZSwgIk5XIikgfiA0MTA4ODE2LA0KICAgIGVuZHNXaXRoKHByb3ZpbmNlLCAiRlMiKSB+IDI5Mjg5MDMsDQogICAgZW5kc1dpdGgocHJvdmluY2UsICJOQyIpIH4gMTI5Mjc4NikpDQoNCiNDYWxjdWxhdGUgY2FzZXMvMTAwaw0KaW5mZWN0MTAwayA8LQ0KICBpbmZlY3QxMDBrICU+JQ0KICBtdXRhdGUoaW5mZWN0MTAwayA9IHJvdW5kKGNvdW50L3BvcHVsYXRpb24gKiAxMDAwMDAsIGRpZ2l0cyA9IDIpKQ0KDQojcGxvdA0KaW5mZWN0MTAwa3Bsb3QgPC0NCiAgZ2dwbG90KGluZmVjdDEwMGssIGFlcyh5ID0gcmVvcmRlcihwcm92aW5jZSwgaW5mZWN0MTAwayksIGluZmVjdDEwMGspKSArDQogIGdlb21faGlzdG9ncmFtKHN0YXQgPSAiaWRlbnRpdHkiLA0KICAgICAgICAgICAgICAgICBmaWxsID0gInJlZCIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGluZmVjdDEwMGspLA0KICAgICAgICAgICAgaGp1c3QgPSAxKSArDQogIGxhYnMoeD0iIiwgeT0iIiwNCiAgICAgICB0aXRsZSA9ICJQcm92aW5jZSBJbmZlY3Rpb25zIHBlciAxMDBLIFBvcHVsYXRpb24iKSArDQogIHRoZW1lKGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpDQogIA0KaW5mZWN0MTAwa3Bsb3QNCg0KYGBgDQoNCkZvciB0aGUgY29uZmlybWVkIGFuZCBhY3RpdmUgY2FzZSB0cmVuZHMgYnkgcHJvdmluY2UgcGxvdHMgdGhlIHByb3ZpbmNlLSBjYXNlcywgZGVhdGhzIGFuZCByZWNvdmVyaWVzIGRhdGEgc2V0cyB3ZXJlIGVhY2ggY29udmVydGVkIHRvIGEgJ2xvbmcnIGZvcm1hdCB3aGVyZS1hZnRlciBpdCB3YXMgam9pbmVkIG9uIHRoZSBkYXRlLiBBZ2FpbiB0aGUgZm9ybXVsYSwgYGFjdGl2ZSBjYXNlcyA9IHRvdGFsIGNhc2VzIC0gdG90YWwgcmVjb3ZlcmVkIC0gdG90YWwgZGVhdGhzYCwgd2VyZSB1c2VkIHRvIGNhbGN1bGF0ZSB0aGUgYWN0aXZlIGNhc2VzIGNvbHVtbi4gVGhlIGRhdGEgd2VyZSB0aGVuIHBsb3R0ZWQgYXMgbGluZSBncmFwaHMgYW5kIGZhY2V0ZWQgYnkgcHJvdmluY2UuDQoNCmBgYHtyfQ0KDQojQ29udmVydCBwcm92aW5jZSBjYXNlcywgcmVjb3ZlcmllcyAmIGRlYXRocyBmcm9tIHdpZGUgdG8gbG9uZw0KDQpwY2FzZXNsb25nIDwtDQogIHByb3ZpbmNlY2FzZXNDU1YgJT4lDQogIHNlbGVjdChkYXRlLCBFQzpXQykgJT4lDQogIGdhdGhlcihwcm92aW5jZSwgY29uZmlybWVkX2Nhc2VzLCBFQzpXQykNCg0KcGRlYXRoc2xvbmcgPC0NCiAgcHJvdmluY2VkZWF0aENTViAlPiUNCiAgc2VsZWN0KGRhdGUsIEVDOldDKSAlPiUNCiAgZ2F0aGVyKHByb3ZpbmNlLCBjb25maXJtZWRfZGVhdGhzLCBFQzpXQykNCg0KcHJlY292ZXJpZXNsb25nIDwtDQogIHByb3ZpbmNlcmVjQ1NWICU+JQ0KICBzZWxlY3QoZGF0ZSwgRUM6V0MpICU+JQ0KICBnYXRoZXIocHJvdmluY2UsIGNvbmZpcm1lZF9yZWNvdmVyaWVzLCBFQzpXQykNCg0KI0pvaW4gdGFibGVzIG9uIGRhdGUNCnBhY3RpdmVjYXNlcyA8LSANCiAgaW5uZXJfam9pbihwY2FzZXNsb25nLCBwZGVhdGhzbG9uZywgb24gPSBkYXRlKSAlPiUgDQogIGlubmVyX2pvaW4ocHJlY292ZXJpZXNsb25nLCBvbiA9IGRhdGUpDQoNCiNDYWxjdWxhdGUgZGFpbHkgYWN0aXZlIGNhc2VzIGNvbHVtbiAoQWN0aXZlIGNhc2VzID0gdG90YWwgY2FzZXMgLSB0b3RhbCByZWNvdmVyZWQgLSB0b3RhbCBkZWF0aHMpDQpwYWN0aXZlY2FzZXMgPC0NCiAgcGFjdGl2ZWNhc2VzICU+JQ0KICBtdXRhdGUoYWN0aXZlX2Nhc2VzID0gY29uZmlybWVkX2Nhc2VzIC0gY29uZmlybWVkX3JlY292ZXJpZXMgLSBjb25maXJtZWRfZGVhdGhzKQ0KDQojcGxvdA0KcGFjdGl2ZXBsb3QgPC0NCiAgZ2dwbG90KHBhY3RpdmVjYXNlcykgKw0KICBnZW9tX2xpbmUoIGFlcyhkYXRlLCBhY3RpdmVfY2FzZXMpLA0KICAgICAgICAgICAgIGNvbG9yID0gImJsdWUiLCANCiAgICAgICAgICAgICBzaXplID0gMSkgKw0KICBnZW9tX2xpbmUoYWVzKGRhdGUsIGNvbmZpcm1lZF9jYXNlcyksDQogICAgICAgICAgICBjb2xvciA9ICJyZWQiLA0KICAgICAgICAgICAgc2l6ZSA9IDEpICsNCiAgbGFicyh4PSIiLCB5PSIiLA0KICAgICAgIHRpdGxlID0gIkNvbmZpcm1lZCBhbmQgQWN0aXZlIENhc2VzIGJ5IFByb3ZpbmNlIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsNCiAgZmFjZXRfd3JhcCh+cHJvdmluY2UpICsNCiAgdGhlbWUoYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpKQ0KDQpnZ3Bsb3RseShwYWN0aXZlcGxvdCwgd2lkdGggPSAxMDAwLCBoZWlnaHQgPSA4MDApDQoNCmBgYA0KDQpCYXIgY2hhcnQgc2hvd2luZyB0aGUgYXZlcmFnZSBkYWlseSB0ZXN0cyBwZXIgd2Vlay4gSW4gb3JkZXIgdG8gcGxvdCB0aGUgYmFyIGNoYXJ0LCBkYXRhIGZyb20gdGhlIGBzb3V0aF9hZnJpY2FfZGF0YWAgZGF0YWZyYW1lIGlzIG5lZWRlZC4gVGhlIGRhdGEgZnJhbWUgYGRhaWx5X3Rlc3RfcGVyX3dlZWtfZGZgIHdhcyBjcmVhdGVkIHNvIHRoYXQgYHNvdXRoX2FmcmljYV9kYXRhYCBjb3VsZCBiZSBtdXRhdGVkIGJ5IHJlcGxhY2luZyAiTkEiIHZhbHVlcyB3aXRoIDAuIFRoaXMgd2FzIGRvbmUgaW4gb3JkZXIgdG8gY2FsY3VsYXRlIHdlZWtseSBhdmVyYWdlcy4gDQoNCmBgYHtyIEF2ZXJhZ2VfZGFpbHlfdGVzdHNfcGVyX3dlZWssIGZpZy5oZWlnaHQ9MTUsIGZpZy53aWR0aD0xMCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9DQogDQojIENyZWF0ZSBhIGxpc3Qgd2hlcmUgYWxsICJOQSIgdmFsdWVzIGFyZSByZXBsYWNlZCB3aXRoIDAgDQoNCmRhaWx5X3Rlc3RfcGVyX3dlZWtfZGYgPC0gc291dGhfYWZyaWNhX2RhdGEgJT4lIA0KICBtdXRhdGVfYXQoYygxOjYwKSwgfnJlcGxhY2UoLiwgaXMubmEoLiksIDApKQ0KDQojIENyZWF0ZSBsaXN0IHdoZXJlIGRhdGEgaXMgZ3JvdXBlZCBieSB3ZWVrIGFuZCB0aGUgYXZlcmFnZSBvZiBlYWNoIHdlZWsncyBkYWlseSBuZXcgdGVzdHMgY2FsY3VsYXRlZA0KDQpkYWlseV90ZXN0X3Blcl93ZWVrX2RmIDwtIGRhaWx5X3Rlc3RfcGVyX3dlZWtfZGYgJT4lIA0KICBncm91cF9ieSh3ZWVrID0gY3V0KHBhcnNlX2RhdGVfdGltZShkYXRlLCAiWSUvbSUvZCUiKSwgIndlZWsiKSkgJT4lIA0KICBzdW1tYXJpc2UodmFsdWUgPSBtZWFuKG5ld190ZXN0c19zbW9vdGhlZCkpDQoNCiMgUGxvdCBiYXIgY2hhcnQNCg0KYXZnX2RhaWx5X3Rlc3RzX3Blcl93ZWVrX3Bsb3QgPC0gDQogIGdncGxvdChkYWlseV90ZXN0X3Blcl93ZWVrX2RmLCBtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKHdlZWssIGRlc2Mod2VlaykpLCB5ID0gdmFsdWUpKSArDQogIGdlb21fY29sKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIgLGZpbGwgPSAicmVkIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQodmFsdWUpKSwgdmp1c3QgPSAwLjEsIGhqdXN0ID0gMCkNCiAgDQojIEZsaXAgdGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBiYXIgY2hhcnQNCg0KYXZnX2RhaWx5X3Rlc3RzX3Blcl93ZWVrX3Bsb3QgKyBjb29yZF9mbGlwKCkgKw0KICBnZ3RpdGxlKCJBdmVyYWdlIERhaWx5IFRlc3RzIFBlciBXZWVrIikgKw0KICB4bGFiKCJXZWVrIikgKyANCiAgeWxhYigiIikgKw0KICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDApKQ0KDQpgYGANCg0KQmFyIGNoYXJ0IHNob3dpbmcgdGhlIGF2ZXJhZ2UgZGFpbHkgcG9zaXRpdmUgY2FzZXMgcGVyIHdlZWsuIEluIG9yZGVyIHRvIHBsb3QgdGhpcyBiYXIgY2hhcnQsIHRoZSAiTkEiIHZhbHVlcyBuZWVkZWQgdG8gYmUgcmVwbGFjZWQgYXMgd2VsbCB0byBjb3JyZWN0bHkgY2FsY3VsYXRlIHRoZSB3ZWVrbHkgYXZlcmFnZXMgZm9yIGRhaWx5IHBvc2l0aXZlIGNhc2VzLiANCg0KYGBge3IgQXZlcmFnZV9kYWlseV9wb3NpdGl2ZXNfcGVyX3dlZWssIGZpZy5oZWlnaHQ9MTUsIGZpZy53aWR0aD0xMCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9DQoNCiMgQ3JlYXRlIGEgbGlzdCB3aGVyZSBhbGwgIk5BIiB2YWx1ZXMgYXJlIHJlcGxhY2VkIHdpdGggMCANCg0KZGFpbHlfcG9zaXRpdmVfY2FzZV9wZXJfd2VlayA8LSBzb3V0aF9hZnJpY2FfZGF0YSAlPiUgDQogIG11dGF0ZV9hdChjKDE6NjApLCB+cmVwbGFjZSguLCBpcy5uYSguKSwgMCkpDQoNCiMgQ3JlYXRlIGxpc3Qgd2hlcmUgZGF0YSBpcyBncm91cGVkIGJ5IHdlZWsgYW5kIHRoZSBhdmVyYWdlIG9mIGVhY2ggd2VlaydzIGRhaWx5IHBvc2l0aXZlIGNhc2VzIGFyZSBjYWxjdWxhdGVkDQoNCmRhaWx5X3Bvc2l0aXZlX2Nhc2VfcGVyX3dlZWsgPC0gZGFpbHlfcG9zaXRpdmVfY2FzZV9wZXJfd2VlayAlPiUgDQogIGdyb3VwX2J5KHdlZWsgPSBjdXQocGFyc2VfZGF0ZV90aW1lKGRhdGUsICJZJS9tJS9kJSIpLCAid2VlayIpKSAlPiUgDQogIHN1bW1hcmlzZSh2YWx1ZSA9IG1lYW4obmV3X2Nhc2VzX3Ntb290aGVkKSkNCg0KIyBQbG90IGJhciBjaGFydA0KDQpnZ3Bsb3QoZGF0YSA9IGRhaWx5X3Bvc2l0aXZlX2Nhc2VfcGVyX3dlZWssIG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIod2VlaywgZGVzYyh3ZWVrKSksIHkgPSB2YWx1ZSkpICsgDQogIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIiwgZmlsbCA9ICJyZWQiKSArDQogIHhsYWIoIldlZWsiKSArIA0KICB5bGFiKCIiKSArIA0KICBnZ3RpdGxlKCJBdmVyYWdlIERhaWx5IFBvc2l0aXZlcyBQZXIgV2VlayIpICsgIA0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQodmFsdWUpKSwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMCkgKw0KICBjb29yZF9mbGlwKCkNCg0KYGBgDQoNCkJhciBjaGFydCBzaG93aW5nIHRoZSBudW1iZXIgb2YgdGVzdHMgY29uZHVjdGVkIHBlciBwb3NpdGl2ZSBjYXNlICh3ZWVrbHkpLiBJbiBvcmRlciB0byBwbG90IHRoaXMgYmFyIGNoYXJ0LCB0aGUgIk5BIiB2YWx1ZXMgbmVlZGVkIHRvIGJlIHJlcGxhY2VkIGFzIHdlbGwgdG8gY29ycmVjdGx5IGNhbGN1bGF0ZSB0aGUgd2Vla2x5IGF2ZXJhZ2VzIGZvciB0aGUgbnVtYmVyIG9mIHRlc3RzIHBlciBwb3NpdGl2ZSBjYXNlLg0KDQpgYGB7ciBOb19vZl90ZXN0c19wZXJfcG9zaXRpdmVfY2FzZV8od2Vla2x5KSwgZmlnLmhlaWdodD0xNSwgZmlnLndpZHRoPTEwLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCg0KIyBDcmVhdGUgYSBsaXN0IHdoZXJlIGFsbCAiTkEiIHZhbHVlcyBhcmUgcmVwbGFjZWQgd2l0aCAwIA0KDQp0ZXN0X3Blcl9wb3NpdGl2ZV9jYXNlIDwtIHNvdXRoX2FmcmljYV9kYXRhICU+JSANCiAgbXV0YXRlX2F0KGMoMTo2MCksIH5yZXBsYWNlKC4sIGlzLm5hKC4pLCAwKSkNCg0KIyBDcmVhdGUgbGlzdCB3aGVyZSBkYXRhIGlzIGdyb3VwZWQgYnkgd2VlayBhbmQgdGhlIGF2ZXJhZ2Ugb2YgZWFjaCB3ZWVrJ3MgdGVzdHMgcGVyIHBvc2l0aXZlIGNhc2UgYXJlIGNhbGN1bGF0ZWQNCg0KdGVzdF9wZXJfcG9zaXRpdmVfY2FzZSA8LSB0ZXN0X3Blcl9wb3NpdGl2ZV9jYXNlICU+JSANCiAgZ3JvdXBfYnkod2VlayA9IGN1dChwYXJzZV9kYXRlX3RpbWUoZGF0ZSwgIlklL20lL2QlIiksICJ3ZWVrIikpICU+JSANCiAgc3VtbWFyaXNlKHZhbHVlID0gbWVhbihuZXdfdGVzdHNfc21vb3RoZWQpIC8gbWVhbihuZXdfY2FzZXNfc21vb3RoZWQpKQ0KDQojIFBsb3QgYmFyIGNoYXJ0DQoNCmdncGxvdChkYXRhID0gdGVzdF9wZXJfcG9zaXRpdmVfY2FzZSwgbWFwcGluZyA9IA0KICAgICAgICAgICAgICBhZXMoeCA9IHJlb3JkZXIod2VlaywgZGVzYyh3ZWVrKSksIHkgPSB2YWx1ZSkpICsgDQogIGdlb21fY29sKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIsIGZpbGwgPSAicmVkIikgKw0KICB4bGFiKCJXZWVrIikgKyANCiAgeWxhYigiIikgKyANCiAgZ2d0aXRsZSgiTnVtYmVyIG9mIFRlc3RzIHBlciBQb3NpdGl2ZSBDYXNlIChXZWVrbHkpIikgKyAgDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZCh2YWx1ZSwyKSwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMCkpICsNCiAgY29vcmRfZmxpcCgpDQoNCmBgYA0KDQpQb2ludCBjaGFydCBzaG93aW5nIHRoZSBkYWlseSB0ZXN0cyBhbmQgcG9zaXRpdmUgY2FzZXMuIEluIG9yZGVyIHRvIHBsb3QgdGhpcyBwb2ludCBjaGFydCwgdGhlIGRhdGEgZnJhbWUgYHNvdXRoX2FmcmljYV9kYXRhYCB3YXMgdXNlZCBhbG9uZyB3aXRoIHRoZSBgZ2dwbG90YCBwYWNrYWdlLiANCg0KYGBge3IgRGFpbHlfdGVzdHNfYW5kX3Bvc2l0aXZlX2Nhc2VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCg0KIyBQbG90IHBvaW50IGNoYXJ0DQoNCmdncGxvdChkYXRhID0gc291dGhfYWZyaWNhX2RhdGEsIG1hcHBpbmcgPSBhZXMoeCA9IHJvdW5kX2RhdGUocGFyc2VfZGF0ZV90aW1lKGRhdGUsICJZJS9tJS9kJSIpLCB1bml0ID0gImRheSIpLCB5ID0gbmV3X3Rlc3RzX3Ntb290aGVkKSwgc2l6ZSA9IG5ld19jYXNlc19zbW9vdGhlZCkgKyANCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IG5ld19jYXNlc19zbW9vdGhlZCksIGNvbCA9ICJ0b21hdG8zIiwgYWxwaGEgPSAwLjcsIHNob3cubGVnZW5kID0gRikgKw0KICB4bGFiKCIiKSArIA0KICB5bGFiKCJOdW1iZXIgb2YgVGVzdHMgcGVyIERheSIpICsNCiAgc2NhbGVfcmFkaXVzKCkgKw0KICBnZ3RpdGxlKCJEYWlseSBUZXN0cyBhbmQgUG9zaXRpdmUgQ2FzZXMiKQ0KDQpgYGANCg0KQmFyIGNoYXJ0IHNob3dpbmcgdGhlIGNvbmZpcm1lZCBpbmZlY3Rpb25zIGZvciB0aGUgbGFzdCAyMCBkYXlzLiBJbiBvcmRlciB0byBwbG90IHRoaXMgYmFyIGNoYXJ0LCB0aGUgZGF0YSBmcmFtZSBgY29uZmlybWVkX2luZmVjdGlvbnNfbGFzdF90d2VudHlfZGF5c2Agd2FzIGNyZWF0ZWQuIFRoaXMgZGF0YSBmcmFtZSB3YXMgZGVyaXZlZCBmcm9tIHRoZSBgc291dGhfYWZyaWNhX2RhdGFgIGRhdGEgZnJhbWUgd2hpY2ggd2FzIGZpbHRlcmVkIGFuZCBhcnJhbmdlZCBzbyB0aGF0IG9ubHkgdGhlIGxhc3QgdHdlbnR5IGRheXMnIGNvbmZpcm1lZCBpbmZlY3Rpb25zIHdlcmUgcGxvdHRlZCBpbiBkZXNjZW5kaW5nIG9yZGVyIG9mIGRhdGUuIA0KDQpgYGB7ciBDb25maXJtZWRfaW5mZWN0aW9uc18obGFzdF8yMF9kYXlzKSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9DQoNCiMgRmlsdGVyIGRhdGEtc2V0IHRvIG9idGFpbiBjb25maXJtZWQgaW5mZWN0aW9uIGZvciB0aGUgbGFzdCB0d2VudHkgZGF5cw0KDQpjb25maXJtZWRfaW5mZWN0aW9uc19sYXN0X3R3ZW50eV9kYXlzIDwtIA0KICBmaWx0ZXIoYXJyYW5nZShzb3V0aF9hZnJpY2FfZGF0YSwgZGF0ZSksIGJldHdlZW4ocm93X251bWJlcigpLCBuKCktMTksIG4oKSkpDQoNCiMgUGxvdCBiYXIgY2hhcnQNCg0KZ2dwbG90KGRhdGEgPSBjb25maXJtZWRfaW5mZWN0aW9uc19sYXN0X3R3ZW50eV9kYXlzLCBtYXBwaW5nID0gDQogIGFlcyh4ID0gcmVvcmRlcihkYXRlLCBkZXNjKGRhdGUpKSwgeSA9IG5ld19jYXNlcykpICsNCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAic3RhY2siLCBmaWxsID0gInJlZCIpICsNCiAgeGxhYigiIikgKyANCiAgeWxhYigiIikgKyANCiAgZ2d0aXRsZSgiIENvbmZpcm1lZCBJbmZlY3Rpb25zIChMYXN0IDIwIERheXMpIikgKyAgDQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBuZXdfY2FzZXMpLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAwKSArDQogIGNvb3JkX2ZsaXAoKQ0KDQpgYGANCg0KQmFyIGNoYXJ0IHNob3dpbmcgdGhlIHBvc2l0aXZpdHkgcmF0ZTogbnVtYmVyIG9mIHRlc3RzIGNvbmR1Y3RlZCB2ZXJzdXMgdGhlIHBlcmNlbnRhZ2UgcG9zaXRpdmUgY2FzZXMuIEluIG9yZGVyIHRvIHBsb3QgdGhpcyBjaGFydCwgd2hpY2ggY29uc2lzdHMgb2YgYm90aCBiYXIgYW5kIGxpbmUgY2hhcnRzLCB0aGUgZGF0YSBmcmFtZSBgcG9zX3JhdGVfZGZgIHdhcyB1c2VkLiBUaGlzIGRhdGEgZnJhbWUgd2FzIGRlcml2ZWQgZnJvbSB0aGUgYHNvdXRoX2FmcmljYV9kYXRhYCBkYXRhIGZyYW1lIGFmdGVyIGl0IHdhcyBmaWx0ZXJlZCBhbmQgYXJyYW5nZWQgYnkgdGFraW5nIHRoZSBsYXN0IG1vbnRoJ3Mgcm93cy4gDQoNCmBgYHtyIFBvc2l0aXZpdHlfUmF0ZV9OdW1iZXJfb2ZfdGVzdHNfdnNfUG9zaXRpdmVfQ2FzZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQ0KDQojIFByZXBhcmUgbGlzdCBmb3IgZ3JhcGggcGxvdCBieSBmaWx0ZXJpbmcgYW5kIHNvcnRpbmcgZGF0YSBmcm9tIHNvdXRoX2FmcmljYV9kYXRhDQoNCnBvc19yYXRlX2RmIDwtDQogIGZpbHRlcihhcnJhbmdlKHNvdXRoX2FmcmljYV9kYXRhLCBkYXRlKSwgYmV0d2Vlbihyb3dfbnVtYmVyKCksIG4oKS0yNiwgbigpKSkNCg0KIyBQbG90IGJhciBjaGFydCBvZiBudW1iZXIgb2YgdGVzdHMgYW5kIGxpbmUgY2hhcnQgb2YgcG9zaXRpdml0eSByYXRlDQoNCmdncGxvdChwb3NfcmF0ZV9kZikgKw0KICBnZW9tX2NvbChhZXMoeCA9IGRhdGUsIHkgPSBuZXdfdGVzdHMpLCBzaXplID0gMSwgZmlsbCA9ICJibHVlIikgKw0KICBnZW9tX2xpbmUoYWVzKHggPSBkYXRlLCB5ID0gKHBvc2l0aXZlX3JhdGUgKiAxMDApKjM1MDApLCBjb2xvdXIgPSAicmVkIiwgc2l6ZSA9IDEuNSwgZ3JvdXAgPSAxKSArDQogIHNjYWxlX3lfY29udGludW91cyhuYW1lID0gIlRlc3RzIENvbmR1Y3RlZCIsIHNlYy5heGlzID0gc2VjX2F4aXMofiAuIC8zNTAwLCBuYW1lID0gIlBvc2l0aXZlIFRlc3RzICglKSIpKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQ0KDQpgYGANCg0KDQpMaW5lIGNoYXJ0IHNob3dpbmcgdGhlIG5hdGlvbmFsIGhvc3BpdGFsIGFkbWlzc2lvbnMuIEluIG9yZGVyIHRvIHBsb3QgdGhpcyBsaW5lIGNoYXJ0LCBkYXRhIGZyb20gdGhlIGB3ZWVrbHlfaG9zcGl0YWxfYWRtaXNzaW9uc2AgZGF0YSBmcmFtZSB3YXMgdXNlZC4gDQoNCmBgYHtyIE5hdGlvbmFsX0hvc3BpdGFsX0FkbWlzc2lvbnMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQ0KDQojIFBsb3QgbGluZSBjaGFydA0KDQpnZ3Bsb3Qod2Vla2x5X2hvc3BpdGFsX2FkbWlzc2lvbnMpICsNCiAgZ2VvbV9saW5lKG1hcHBpbmcgPSBhZXMoeCA9IHJvdW5kX2RhdGUocGFyc2VfZGF0ZV90aW1lKHdlZWtfc3RhcnQsICJZJS9tJS9kJSIpLCB1bml0ID0gIndlZWsiKSwgeSA9IHRvdGFsX25hdGlvbmFsX2hvc3BpdGFsX2FkbWlzc2lvbnMpLCBjb2xvdXIgPSAicmVkIikgKw0KICBnZ3RpdGxlKCJOYXRpb25hbCBIb3NwaXRhbCBBZG1pc3Npb25zIikgKw0KICB4bGFiKCJFUEkgV0VFSyIpICsgDQogIHlsYWIoIiIpDQoNCmBgYA0KDQpUYWJsZSBzaG93aW5nIHB1YmxpYyB2ZXJzdXMgcHJpdmF0ZSB0ZXN0cy4gSW4gb3JkZXIgdG8gY3JlYXRlIHRoaXMgdGFibGUsIHZhcmlvdXMgZGF0YSBwcmVwYXJhdGlvbnMgYW5kIGNhbGN1bGF0aW9ucyB3ZXJlIG1hZGUsIGFsb25nIHdpdGggdGhlIGBwbG90bHlgIHBhY2thZ2UuDQoNCmBgYHtyIFB1YmxpY192c19Qcml2YXRlX1Rlc3RzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCg0KIyBQcmVwYXJlIGFuZCBjYWxjdWxhdGUgZGF0YSBmb3IgdGhlIGNyZWF0aW9uIG9mIHRoZSBwbG90bHkgdGFibGUNCg0KZGYgPC0gDQogIHRlc3RzICU+JSANCiAgc2xpY2UobigpLTEpDQoNCmxhdGVzdF9kYWlseV9yZXBvcnQgPC0gDQogIHRlc3RzICU+JSANCiAgc2xpY2UobigpKQ0KDQp0b3RhbF90ZXN0c192YXIgPC0gDQogIGxhdGVzdF9kYWlseV9yZXBvcnQkY3VtdWxhdGl2ZV90ZXN0cw0KDQp0b3RhbF90ZXN0c192YXJfcGVyY2VudCA8LSANCiAgIjEwMCAoJSkiDQoNCnRvdGFsX3B1YmxpY190ZXN0c192YXIgPC0gDQogIGxhdGVzdF9kYWlseV9yZXBvcnQkY3VtdWxhdGl2ZV90ZXN0c19wdWJsaWMNCg0KdG90YWxfcHVibGljX3Rlc3RzX3Zhcl9wZXJjZW50IDwtIA0KICByb3VuZCgodG90YWxfcHVibGljX3Rlc3RzX3Zhci90b3RhbF90ZXN0c192YXIpICogMTAwLCAyKQ0KDQp0b3RhbF9wcml2YXRlX3Rlc3RzX3ZhciA8LQ0KICBsYXRlc3RfZGFpbHlfcmVwb3J0JGN1bXVsYXRpdmVfdGVzdHNfcHJpdmF0ZQ0KDQp0b3RhbF9wcml2YXRlX3Rlc3RzX3Zhcl9wZXJjZW50IDwtDQogIHJvdW5kKCh0b3RhbF9wcml2YXRlX3Rlc3RzX3Zhci90b3RhbF90ZXN0c192YXIpICogMTAwLCAyKQ0KDQpuZXdfcHVibGljX3Rlc3RzX3ZhciA8LSANCiAgbGF0ZXN0X2RhaWx5X3JlcG9ydCRjdW11bGF0aXZlX3Rlc3RzX3B1YmxpYyANCi0gZGYkY3VtdWxhdGl2ZV90ZXN0c19wdWJsaWMNCg0KbmV3X3ByaXZhdGVfdGVzdHNfdmFyIDwtIA0KICBsYXRlc3RfZGFpbHlfcmVwb3J0JGN1bXVsYXRpdmVfdGVzdHNfcHJpdmF0ZSANCi0gZGYkY3VtdWxhdGl2ZV90ZXN0c19wcml2YXRlDQoNCnRvdGFsX25ld190ZXN0c192YXIgPC0gDQogIG5ld19wdWJsaWNfdGVzdHNfdmFyICsgbmV3X3ByaXZhdGVfdGVzdHNfdmFyDQoNCm5ld19wdWJsaWNfdGVzdHNfdmFyX3BlcmNlbnQgPC0gDQogIHJvdW5kKChuZXdfcHVibGljX3Rlc3RzX3Zhci90b3RhbF9uZXdfdGVzdHNfdmFyKSAqIDEwMCwgMikNCg0KbmV3X3ByaXZhdGVfdGVzdHNfdmFyX3BlcmNlbnQgPC0gDQogIHJvdW5kKChuZXdfcHJpdmF0ZV90ZXN0c192YXIvdG90YWxfbmV3X3Rlc3RzX3ZhcikgKiAxMDAsIDIpDQoNCnRhYiA8LSANCiAgbWF0cml4KGModG90YWxfdGVzdHNfdmFyLHRvdGFsX3Rlc3RzX3Zhcl9wZXJjZW50LHRvdGFsX3B1YmxpY190ZXN0c192YXIsIHRvdGFsX3B1YmxpY190ZXN0c192YXJfcGVyY2VudCwgdG90YWxfcHJpdmF0ZV90ZXN0c192YXIsIHRvdGFsX3ByaXZhdGVfdGVzdHNfdmFyX3BlcmNlbnQsIG5ld19wdWJsaWNfdGVzdHNfdmFyLCBuZXdfcHVibGljX3Rlc3RzX3Zhcl9wZXJjZW50LCBuZXdfcHJpdmF0ZV90ZXN0c192YXIsIG5ld19wcml2YXRlX3Rlc3RzX3Zhcl9wZXJjZW50KSwgbmNvbD0yLCBieXJvdz1UUlVFKQ0KDQpyb3duYW1lcyh0YWIpIDwtIA0KICBjKCdUb3RhbCBUZXN0cycsJ1RvdGFsIFB1YmxpYyBUZXN0cycsJ1RvdGFsIFByaXZhdGUgVGVzdHMnLA0KICAgICAgICAgICAgICAgICAgICdOZXcgUHVibGljIFRlc3RzJywgJ05ldyBQcml2YXRlIFRlc3RzJykNCnRhYiA8LSANCiAgYXMudGFibGUodGFiKQ0KDQpwdWJsaWNfdnNfcHJpdmF0ZV90YWJsZSA8LSANCiAgYXMuZGF0YS5mcmFtZS5tYXRyaXgodGFiKQ0KDQojIENyZWF0ZSBhIHBsb3RseSB0YWJsZSBmb3IgcHVibGljX3ZzX3ByaXZhdGVfdGFibGUgbGlzdA0KDQpwdnAgPC0gDQogIHBsb3RfbHkoDQogIHdpZHRoID0gMTAwMCwNCiAgaGVpZ2h0ID0gNTAwLA0KICB0eXBlID0gJ3RhYmxlJywNCiAgaGVhZGVyID0gbGlzdCgNCiAgICB2YWx1ZXMgPSBjKCI8Yj5QdWJsaWMgdnMuIFByaXZhdGUgVGVzdHM8L2I+IiwgbmFtZXMocHVibGljX3ZzX3ByaXZhdGVfdGFibGUpKSwNCiAgICBhbGlnbiA9IGMoJ2xlZnQnLCByZXAoJ2NlbnRlcicsIG5jb2wocHVibGljX3ZzX3ByaXZhdGVfdGFibGUpKSksDQogICAgbGluZSA9IGxpc3Qod2lkdGggPSAxLCBjb2xvciA9ICdibGFjaycpLA0KICAgIGZpbGwgPSBsaXN0KGNvbG9yID0gJ3JnYigwLCAxNjUsIDIxOSknKSwNCiAgICBmb250ID0gbGlzdChmYW1pbHkgPSAiQXJpYWwiLCBzaXplID0gMTQsIGNvbG9yID0gIndoaXRlIikNCiAgKSwNCiAgY2VsbHMgPSBsaXN0KA0KICAgIHZhbHVlcyA9IHJiaW5kKA0KICAgICAgcm93bmFtZXMocHVibGljX3ZzX3ByaXZhdGVfdGFibGUpLCANCiAgICAgIHQoYXMubWF0cml4KHVubmFtZShwdWJsaWNfdnNfcHJpdmF0ZV90YWJsZSkpKQ0KICAgICksDQogICAgYWxpZ24gPSBjKCdsZWZ0JywgcmVwKCdjZW50ZXInLCBuY29sKHB1YmxpY192c19wcml2YXRlX3RhYmxlKSkpLA0KICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gImJsYWNrIiwgd2lkdGggPSAxKSwNCiAgICBmaWxsID0gbGlzdChjb2xvciA9IGMoJ3JnYigwLCAyMjAsIDIxOSknLCAncmdiYSgyMjgsIDIyMiwgMjQ5LCAwLjY1KScpKSwNCiAgICBmb250ID0gbGlzdChmYW1pbHkgPSAiQXJpYWwiLCBzaXplID0gMTIsIGNvbG9yID0gYygiYmxhY2siKSkNCiAgKSkNCg0KcHZwDQoNCmBgYA0KDQpQb2ludCBjaGFydCBzaG93aW5nIHRoZSBjdXJyZW50IHJlcHJvZHVjdGlvbiByYXRlIGVzdGltYXRlcyBmb3IgU291dGggQWZyaWNhLiBJbiBvcmRlciB0byBwbG90IHRoaXMgcG9pbnQgY2hhcnQsIHRoZSBkYXRhIGZyYW1lIGBzb3V0aF9hZnJpY2FfZGF0YV9yZWZpbmVkYCBhbmQgdGhlIGBnZ3Bsb3RgIHBhY2thZ2Ugd2FzIHV0aWxpemVkLiBUaGUgZGF0YSBmcmFtZSwgYHNvdXRoX2FmcmljYV9kYXRhX3JlZmluZWRgLCB3YXMgZGVyaXZlZCBmcm9tIHRoZSBgc291dGhfYWZyaWNhX2RhdGFgIGRhdGEgZnJhbWUuIFRoZSBkYXRhIGZyYW1lIHdhcyBtb2RpZmllZCBzbyB0aGF0IGl0IHdpbGwgb25seSBjb250YWluIHRoZSBgZGF0ZWAgYW5kIGByZXByb2R1Y3Rpb25fcmF0ZWAgdmFyaWFibGVzLiANCg0KYGBge3IgQ3VycmVudF9SdF9lc3RpbWF0ZXNfZm9yX1NvdXRoX0FmcmljYSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9DQoNCiMgUGxvdCBhIHBvaW50IGNoYXJ0DQoNCmdncGxvdChkYXRhID0gc291dGhfYWZyaWNhX2RhdGFfcmVmaW5lZCwgbWFwcGluZyA9IA0KICAgICAgICAgYWVzKHggPSByb3VuZF9kYXRlKHBhcnNlX2RhdGVfdGltZShkYXRlLCAiWSUvbSUvZCUiKSwgdW5pdCA9ICJkYXkiKSwgeSA9IHJlcHJvZHVjdGlvbl9yYXRlLCANCiAgZ3JvdXAgPSAxKSkgKyANCiAgZ2VvbV9yaWJib24oYWVzKHltaW4gPSByZXByb2R1Y3Rpb25fcmF0ZSAtIDAuMSwgeW1heCA9IHJlcHJvZHVjdGlvbl9yYXRlICsgMC4xKSwgZmlsbCA9ICJncmV5NzAiKSArDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9ICJyZWQiKSwgc2hvdy5sZWdlbmQgPSBGLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKCkpICsNCiAgZ2d0aXRsZSgiQ3VycmVudCBSdCBFc3RpbWF0ZXMgZm9yIFNvdXRoIEFmcmljYSIpICsNCiAgeGxhYigiIikgKyANCiAgeWxhYigiIikNCg0KYGBgDQoNCkJhciBjaGFydCBzaG93aW5nIHRoZSBleGNlc3MgbnVtYmVyIG9mIGRlYXRocyBpbiBTb3V0aCBBZnJpY2EuIEluIG9yZGVyIHRvIHBsb3QgdGhpcyBiYXIgY2hhcnQsIGRhdGEgZnJvbSB0aGUgZGF0YSBmcmFtZSBgcHJvdmluY2VkZWF0aENTVmAgYWxvbmcgd2l0aCB0aGUgYGdncGxvdGAgYW5kIGBkcGx5ZXJgIHBhY2thZ2VzIHdlcmUgdXNlZC4gVGhlIGxhc3Qgcm93IGNvbnRhaW5pbmcgdGhlIGNvdmlkLTE5IGRlYXRoIHRvdGFscyBmb3IgU291dGggQWZyaWNhIGFzIGEgd2hvbGUgd2FzIHNsaWNlZCBhbmQgc3RvcmVkIGluIHRoZSBkYXRhIGZyYW1lIGB0ZW1wX2RmYC4gVGhlcmVhZnRlciwgYSBuZXcgY29sdW1uIGNhbGxlZCBgZXhjZXNzX2RlYXRoX3RvdGFsX2NvbHVtbmAgd2l0aCB0aGUgZXhjZXNzIGRlYXRoIHRvdGFsIGZvciBTb3V0aCBBZnJpY2Egd2FzIGFkZGVkIHRvIGB0ZW1wX2RmYC4gVGhlcmVhZnRlciwgdGhlIGJhciBjaGFydHMgd2VyZSBzdGFja2VkIG9uIHRvcCBvZiBlYWNoIG90aGVyIGluIG9yZGVyIHRvIGlsbHVzdHJhdGUgdGhlIGRpZmZlcmVuY2UgaW4gZXhjZXNzIHRvdGFsIGRlYXRocyBhbmQgY292aWQtMTkgdG90YWwgZGVhdGhzIGZvciBTb3V0aCBBZnJpY2EuIA0KDQpgYGB7ciBFeGNlc3NfRGVhdGhzX1NvdXRoX0FmcmljYSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9DQoNCiMgR2V0IHRoZSBsYXN0IHJvdyBpbiBkYXRhIGZyYW1lIGFuZCBzdG9yZSBpdCBpbiB0ZW1wX2RmDQoNCnRlbXBfZGYgPC0gDQogIHByb3ZpbmNlZGVhdGhDU1YgJT4lIA0KICBzbGljZShuKCkpDQoNCiMgQWRkIG5ldyBjb2x1bW4gdG8gdGVtcF9kZg0KDQp0ZW1wX2RmIDwtIA0KICB0ZW1wX2RmICU+JQ0KICBhZGRfY29sdW1uKGV4Y2Vzc19kZWF0aF90b3RhbF9jb2x1bW4gPSBleGNlc3NfZGVhdGhzX1JTQSRleGNlc3NfZGVhdGhfdG90YWxzKQ0KDQojIFBsb3QgYmFyIGNoYXJ0cyBzdGFja2VkIG9uIHRvcCBvZiBlYWNoIG90aGVyDQoNCmdncGxvdCh0ZW1wX2RmLCBhZXMoeCA9IGRhdGUpKSArDQogIGdlb21fY29sKG1hcHBpbmcgPSBhZXMoeSA9IHJvdW5kKGFzLm51bWVyaWMoZXhjZXNzX2RlYXRoX3RvdGFsX2NvbHVtbikpKSwgZmlsbCA9ICJibHVlIikgKw0KICBnZW9tX2NvbCh0ZW1wX2RmLCBtYXBwaW5nID0gYWVzKHkgPSB0b3RhbCksIGZpbGwgPSAicmVkIikgKw0KICBnZW9tX3RleHQoYWVzKHkgPSByb3VuZChhcy5udW1lcmljKGV4Y2Vzc19kZWF0aF90b3RhbF9jb2x1bW4pKSwgbGFiZWwgPSByb3VuZChhcy5udW1lcmljKGV4Y2Vzc19kZWF0aF90b3RhbF9jb2x1bW4pKSwgdmp1c3QgPSAtMC41LCBoanVzdCA9IDAuNSkpICsNCiAgZ2VvbV90ZXh0KGFlcyh5ID0gdG90YWwsIGxhYmVsID0gdG90YWwpLCB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41KSArDQogIGdndGl0bGUoIkV4Y2VzcyBEZWF0aHMgKFNvdXRoIEFmcmljYSkiKSArDQogIHhsYWIoIiIpICsgDQogIHlsYWIoIiIpICsgDQogIGNvb3JkX2NhcnRlc2lhbihleHBhbmQgPSBULCB5bGltID0gYygwLDIwMDAwMCkpDQoNCmBgYA0KDQpCYXIgY2hhcnRzIHNob3dpbmcgdGhlIGV4Y2VzcyBkZWF0aHMgYW5kIGNvdmlkLTE5IGRlYXRocyBwZXIgcHJvdmluY2UgaW4gU291dGggQWZyaWNhLiBJbiBvcmRlciB0byBwbG90IHRoaXMgYmFyIGNoYXJ0LCBkYXRhIGZyb20gdGhlIGRhdGEgZnJhbWUgYHByb3ZpbmNlZGVhdGhDU1ZgIGFsb25nIHdpdGggdGhlIGBnZ3Bsb3RgIGFuZCBgZHBseWVyYCBwYWNrYWdlcyB3ZXJlIHVzZWQuIFRoZSBsYXN0IHJvdyBjb250YWluaW5nIHRoZSBjb3ZpZC0xOSBkZWF0aCB0b3RhbHMgcGVyIHByb3ZpbmNlIHdhcyBzbGljZWQgYW5kIHN0b3JlZCBpbiB0aGUgZGF0YSBmcmFtZSBgZGZgLiBXaGljaCB3YXMgdGhlbiBjb252ZXJ0ZWQgZnJvbSBhIGBkYXRhLmZyYW1lYCB0byBhIGBkYXRhLnRhYmxlYCBieSByZWZlcmVuY2Ugd2l0aCB0aGUgdXNlIG9mIHRoZSBgc2V0RFQoKWAgZnVuY3Rpb24uIFRoZXJlYWZ0ZXIsIHRoZSBkYXRhIHdhcyB0cmFuc2Zvcm1lZCBpbnRvIGEgc2VwYXJhdGUgY29sdW1uIHZpYSB0aGUgYHN0YWNrKClgIGZ1bmN0aW9uLiBVbm5lY2Vzc2FyeSByb3dzIHdlcmUgcmVtb3ZlZCBpbiBvcmRlciBmb3IgdGhlIHRhYmxlcyBgZXhjZXNzX2RlYXRoc19wcm92YCBhbmQgYHRlbXBgIHRvIGJlIG1lcmdlZCBpbnRvIHRhYmxlIGBqb2luZWRfZGZgIGJ5IHZhcmlhYmxlcyBsb2NhdGlvbiBhbmQgYHByb3ZfY29kZWAuIFRoZXJlYWZ0ZXIsIHRoZSBiYXIgY2hhcnRzIHdlcmUgc3RhY2tlZCBvbiB0b3Agb2YgZWFjaCBvdGhlciBpbiBvcmRlciB0byBpbGx1c3RyYXRlIHRoZSBkaWZmZXJlbmNlIGluIGV4Y2VzcyBkZWF0aHMgYW5kIGNvdmlkLTE5IGRlYXRocyBwZXIgcHJvdmluY2UuIA0KDQpgYGB7ciBFeGNlc3NfRGVhdGhzX1Byb3ZpbmNlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9DQoNCiMgR2V0IHRoZSBsYXN0IHJvdyBpbiBkYXRhIGZyYW1lIGFuZCBzdG9yZSBpdCBpbiBkZg0KDQpkZiA8LQ0KICBwcm92aW5jZWRlYXRoQ1NWICU+JSANCiAgc2xpY2UobigpKQ0KDQojIENvbnZlcnQgZGF0YS5mcmFtZSBpbnRvIGRhdGEudGFibGUgYnkgcmVmZXJlbmNlDQoNCnNldERUKGRmLGtlZXAucm93bmFtZXMgPSBUKVtdDQoNCiMgVHJhbnNmb3JtIGRhdGEgaW50byBhIHNlcGFyYXRlIGNvbHVtbg0KDQp0ZW1wIDwtIA0KICBzdGFjayhkZikNCg0KIyBSZW1vdmUgdW53YW50ZWQgcm93cw0KDQp0ZW1wIDwtDQogIHRlbXBbLWMoMSwyLDEyLCAxMywxNCksICxkcm9wID0gRl0NCg0KIyBSZW5hbWUgY29sdW1uIG5hbWVzDQoNCmNvbG5hbWVzKHRlbXApIDwtIA0KICBjKCJkZWF0aF90b3RhbHMiLCAicHJvdl9jb2RlIikNCg0KIyBKb2luIGRhdGEgZnJhbWVzOiBleGNlc3NfZGVhdGhzX3Byb3YgJiB0ZW1wIGFuZCBzdG9yZSBpdCBpbiBqb2luZWRfZGYNCg0Kam9pbmVkX2RmIDwtIA0KICBtZXJnZShleGNlc3NfZGVhdGhzX3Byb3YsIHRlbXAsIGJ5LnggPSAibG9jYXRpb24iLCANCiAgICAgICAgICAgICBieS55ID0gInByb3ZfY29kZSIsIGFsbC54ID0gVFJVRSwgYWxsLnkgPSBUUlVFKQ0KDQojIFBsb3QgYmFyIGNoYXJ0cyBzdGFja2VkIG9uIHRvcCBvZiBlYWNoIG90aGVyDQoNCmdncGxvdChqb2luZWRfZGYsIG1hcHBpbmcgPSBhZXMoeCA9IHJlb3JkZXIobG9jYXRpb24sIGRlc2MoYXMubnVtZXJpYyhleGNlc3NfZGVhdGhfdG90YWxzKSkpLCB5ID0gcm91bmQoYXMubnVtZXJpYyhleGNlc3NfZGVhdGhfdG90YWxzKSksIGRpZ2l0cyA9IDIpKSArDQogIGdlb21fY29sKGZpbGwgPSAiYmx1ZSIpICsNCiAgZ2VvbV9jb2woam9pbmVkX2RmLCBtYXBwaW5nID0gYWVzKHkgPSBhcy5udW1lcmljKGRlYXRoX3RvdGFscykpLCBmaWxsID0gInJlZCIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKGFzLm51bWVyaWMoZXhjZXNzX2RlYXRoX3RvdGFscykpLCB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41KSkgKw0KICBnZ3RpdGxlKCJFeGNlc3MgRGVhdGhzIChQcm92aW5jZXMpIikgKw0KICB4bGFiKCIiKSArIA0KICB5bGFiKCIiKSArIA0KICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCw0NTAwMCkpDQoNCmBgYA0KDQpCYXIgY2hhcnQgc2hvd2luZyB0aGUgZXhjZXNzIGRlYXRocyBpbiB0aGUgbWV0cm8gYXJlYXMgaW4gU291dGggQWZyaWNhLiBJbiBvcmRlciB0byBwbG90IHRoaXMgY2hhcnQsIHRoZSBkYXRhIGZyYW1lIGBleGNlc3NfZGVhdGhzX21ldHJvYCB3YXMgdXRpbGl6ZWQgYWxvbmcgd2l0aCB0aGUgYGdncGxvdGAgcGFja2FnZS4gVGhlIGJhciBjaGFydCBpcyBvcmRlcmVkIGZyb20gaGlnaGVzdCBleGNlc3MgZGVhdGggY291bnQgdG8gbG93ZXN0LiAgDQoNCmBgYHtyIEV4Y2Vzc19EZWF0aHNfTWV0cm9zfQ0KDQojIFBsb3QgYmFyIGNoYXJ0DQoNCmdncGxvdChleGNlc3NfZGVhdGhzX21ldHJvLCBtYXBwaW5nID0gYWVzKHggPSByZW9yZGVyKGxvY2F0aW9uLCBkZXNjKGFzLm51bWVyaWMoZXhjZXNzX2RlYXRoX3RvdGFscykpKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gcm91bmQoYXMubnVtZXJpYyhleGNlc3NfZGVhdGhfdG90YWxzKSksIGRpZ2l0cyA9IDIpKSArDQogIGdlb21fY29sKGZpbGwgPSAiYmx1ZSIpICsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKGFzLm51bWVyaWMoZXhjZXNzX2RlYXRoX3RvdGFscykpLCB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMC41KSkgKw0KICBnZ3RpdGxlKCJFeGNlc3MgRGVhdGhzIChNZXRyb3MpIikgKw0KICB4bGFiKCIiKSArIA0KICB5bGFiKCIiKSArIA0KICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwxMzAwMCkpDQoNCmBgYA0KDQpMaW5lIGNoYXJ0IHNob3dpbmcgdGhlIG5ldyBkYWlseSBjb25maXJtZWQgQ292aWQtMTkgY2FzZXMgb24gYSA3LWRheSBtb3ZpbmcgYXZlcmFnZSBzY2FsZSBmb3IgdGhlIHJlZ2lvbnM6IEFmcmljYSwgQXNpYSwgRXVyb3BlLCBOb3J0aCBBbWVyaWNhLCBPY2VhbmlhLCBTb3V0aCBBbWVyaWNhIGFuZCB0aGUgV29ybGQuIEluIG9yZGVyIHRvIHBsb3QgdGhpcyBjaGFydCwgZGF0YSBmcm9tIHRoZSBkYXRhIGZyYW1lIGBuZXdfZGFpbHlfY29uZmlybWVkX2Nhc2VfZGF0YWAgd2FzIHVzZWQsIGFsb25nIHdpdGggdGhlIGBnZ3Bsb3RgIHBhY2thZ2UuIFRoZSBgbmV3X2RhaWx5X2NvbmZpcm1lZF9jYXNlX2RhdGFgIHdhcyBkZXJpdmVkIGZyb20gZmlsdGVyaW5nIHRoZSBsb2NhdGlvbnMgZnJvbSB0aGUgYG93aWRfY292aWRfZGF0YWAgZGF0YSBmcmFtZS4gVGhlIGxpbmUgY2hhcnQgd2FzIHdhcyBncm91cGVkIGJ5IGBsb2NhdGlvbmAgKGkuZS4gcmVnaW9uKS4gDQoNCmBgYHtyIE5ld19kYWlseV9jb25maXJtZWRfQ292aWQtMTlfY2FzZXNfN19kYXlfYXZlcmFnZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0V9DQoNCiMgUGxvdCBsaW5lIGdyYXBoDQoNCmdncGxvdChuZXdfZGFpbHlfY29uZmlybWVkX2Nhc2VfZGF0YSwgbWFwcGluZyA9IA0KICAgICAgICAgYWVzKHggPSByb3VuZF9kYXRlKHBhcnNlX2RhdGVfdGltZShkYXRlLCAiWSUvbSUvZCUiKSwgdW5pdCA9ICJkYXkiKSwgDQogICAgICAgICAgICAgeSA9IG5ld19jYXNlc19zbW9vdGhlZCwgDQogICAgICAgICAgICAgZ3JvdXAgPSBsb2NhdGlvbikpICsgDQogIGdlb21fbGluZShhZXMoY29sb3VyID0gbG9jYXRpb24pKSArDQogIGdndGl0bGUoIk5ldyBEYWlseSBDb25maXJtZWQgQ292aWQtMTkgQ2FzZXM6IDctRGF5IEF2ZXJhZ2UiKSArDQogIHhsYWIoIiIpICsgDQogIHlsYWIoIiIpICsgDQogIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLDEwMDAwMDApKQ0KDQpgYGA=